How do I prevent removal of auth0 session cookie on refresh? - authentication

I have a simple demo app based on SvelteKit in which I am attempting to use auth0 for authentication. The initial authentication works as expected, and is executed like this (I also have a stores.js file where writable stores are created to hold the state of user, isAutheniticated, and popupOpen, and an auth_config.js file to hold my auth0 credentials.):
// authService.js
import { createAuth0Client } from "#auth0/auth0-spa-js";
import { user, isAuthenticated, popupOpen } from "./store";
import config from "../auth_config";
async function createClient() {
let auth0Client = await createAuth0Client({
domain: config.domain,
clientId: config.clientId
});
return auth0Client;
}
async function loginWithPopup(client, options) {
let c = await client.loginWithPopup(options);
user.set(await client.getUser());
isAuthenticated.set(true);
}
function logout(client) {
return client.logout();
}
//+layout.svelte
<script>
import { onMount } from "svelte";
import auth from "../authService";
import { isAuthenticated, user } from "../store";
let auth0Client;
onMount(async () => {
auth0Client = await auth.createClient();
isAuthenticated.set(await auth0Client.isAuthenticated());
user.set(await auth0Client.getUser());
});
function login() {
auth.loginWithPopup(auth0Client);
}
function logout() {
auth.logout(auth0Client);
}
</script>
{#if $isAuthenticated}
<a class="nav-link" href="/#" on:click="{logout}">Log Out</a>
{:else}
<a class="nav-link" href="/#" on:click="{login}">Log In</a>
{/if}
<slot />
And when I login I see a cookie named auth0.MY_CLIENT_ID.is.authenticated and its value is "true".
However, when I refresh the page, the cookie is removed. When I click the login link, the auth0 popup briefly displays but then closes itself and I am logged in again, and the cookie is set again.
It seems to me I am doing something wrong, since the auth0 client knows I am logged in and does not ask for my credentials again, but my app is not aware I am logged in. What is the proper way to hang on to the session cookie so that it does not appear that I am logged out when I refresh?

Researching some more, I came across this very helpful post from the auth0 message boards.
Allowing 3rd party cookies "solved" my problem, but obviously I don't want my end users to have to do that. Instead, I enabled refresh tokens within my application settings on auth0.com, and then within my client I enabled local storage and refresh tokens:
async function createClient() {
let auth0Client = await createAuth0Client({
domain: config.domain,
clientId: config.clientId,
cacheLocation: 'localstorage',
useRefreshTokens: true
});
return auth0Client;
}
Now my authentication survives a refresh (even with 3rd party cookies blocked).

Related

Nuxt 3 JWT authentication using $fetch and Pinia

I'm discovering Nuxt 3 since a few days and I'm trying to do a JWT authentication to a distinct API.
As #nuxtjs/auth-next doesn't seem to be up to date and as I read it was possible to use the new global method fetch in Nuxt 3 instead of #nuxtjs/axios (not up to date also), I thought it won't be too hard to code the authentication myself! But it stays a mystery to me and I only found documentation on Vue project (using Pinia to keep user logged in) and I'm a bit at a lost.
What I would like to achieve:
a login page with email and password, login request send to API (edit: done!)
get JWT token and user info from API (edit: done!) and store both (to keep user logged even if a page is refresh)
set the JWT token globally to header $fetch requests (?) so I don't have to add it to each request
don't allow access to other pages if user is not logged in
Then I reckon I'll have to tackle the refresh token subject, but one step at a time!
It will be really awesome to have some help on this, I'm not a beginner but neither a senior and authentication stuff still frightens me :D
Here is my login.vue page (I'll have to use Vuetify and vee-validate after that but again one step at a time!)
// pages/login.vue
<script setup lang="ts">
import { useAuthStore } from "~/store/auth";
const authStore = useAuthStore();
interface loginForm {
email: string;
password: string;
}
let loginForm: loginForm = {
email: "",
password: "",
};
function login() {
authStore.login(loginForm);
}
</script>
<template>
<v-container>
<form #submit.prevent="login">
<label>E-mail</label>
<input v-model="loginForm.email" required type="email" />
<label>Password</label>
<input v-model="loginForm.password" required type="password" />
<button type="submit">Login</button>
</form>
</v-container>
</template>
The store/auth.ts for now.
// store/auth.ts
import { defineStore } from 'pinia'
import { encodeURL } from '~~/services/utils/functions'
export const useAuthStore = defineStore({
id: 'auth,
state: () => ({
// TODO Initialize state from local storage to enable user to stay logged in
user: '',
token: '',
})
actions: {
async login(loginForm) {
const URL_ENCODED_FORM = encodeURL({
email: loginForm.email,
password: loginForm.password,
});
return await $fetch('api_route', {
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
method: 'POST',
body: URL_ENCODED_FORM
}
}
}
})
i'm gonna share everything, even the parts you marked as done, for completeness sake.
Firstly, you will need something to generate a JWT in the backend, you can do that plainly without any packages, but i would recommend this package for that. Also i'll use objection.js for querying the database, should be easy to understand even if you don't know objection.js
Your login view needs to send a request for the login attempt like this
const token = await $fetch('/api/login', {
method: 'post',
body: {
username: this.username,
password: this.password,
},
});
in my case it requests login.post.ts in /server/api/
import jwt from 'jsonwebtoken';
import { User } from '../models';
export default defineEventHandler(async (event) => {
const body = await useBody(event);
const { id } = await User.query().findOne('username', body.username);
const token: string = await jwt.sign({ id }, 'mysecrettoken');
return token;
});
For the sake of simplicity i didn't query for a password here, this depends on how you generate a user password.
'mysecrettoken' is a token that your users should never get to know, because they could login as everybody else. of course this string can be any string you want, the longer the better.
now your user gets a token as the response, should just be a simple string. i'll write later on how to save this one for future requests.
To make authenticated requests with this token you will need to do requests like this:
$fetch('/api/getauthuser', {
method: 'post',
headers: {
authentication: myJsonWebToken,
},
});
i prefer to add a middleware for accessing the authenticated user in my api endpoints easier. this middleware is named setAuth.ts and is inside the server/middleware folder. it looks like this:
import jwt from 'jsonwebtoken';
export default defineEventHandler(async (event) => {
if (event.req.headers.authentication) {
event.context.auth = { id: await jwt.verify(event.req.headers.authentication, 'mysecrettoken').id };
}
});
What this does is verify that if an authentication header was passed, it checks if the token is valid (with the same secret token you signed the jwt with) and if it is valid, add the userId to the request context for easier endpoint access.
now, in my server/api/getauthuser.ts endpoint in can get the auth user like this
import { User } from '../models';
export default defineEventHandler(async (event) => {
return await User.query().findById(event.context.auth.id)
});
since users can't set the requests context, you can be sure your middleware set this auth.id
you have your basic authentication now.
The token we generated has unlimited lifetime, this might not be a good idea. if this token gets exposed to other people, they have your login indefinitely, explaining further would be out of the scope of this answer tho.
you can save your auth token in the localStorage to access it again on the next pageload. some people consider this a bad practice and prefer cookies to store this. i'll keep it simple and use the localStorage tho.
now for the part that users shouldnt access pages other than login: i set a global middleware in middleware/auth.global.ts (you can also do one that isnt global and specify it for specific pages)
auth.global.ts looks like this:
import { useAuthStore } from '../stores';
export default defineNuxtRouteMiddleware(async (to) => {
const authStore = useAuthStore();
if (to.name !== 'Login' && !localStorage.getItem('auth-token')) {
return navigateTo('/login');
} else if (to.name !== 'Login' && !authStore.user) {
authStore.setAuthUser(await $fetch('/api/getauthuser', {
headers: authHeader,
}));
}
});
I'm using pinia to store the auth user in my authStore, but only if the localstorage has an auth-token (jwt) in it. if it has one and it hasnt been fetched yet, fetch the auth user through the getauthuser endpoint. if it doesnt have an authtoken and the page is not the login page, redirect the user to it
With the help of #Nais_One I managed to do a manual authentication to a third-party API with Nuxt 3 app using client-side rendering (ssr: false, target: 'static' in nuxt.config.ts)
I still have to set the API URL somewhere else and to handle JWT token refresh but the authentication works, as well as getting data from a protected API route with the token in header and redirection when user is not logged.
Here are my finals files:
// pages/login.vue
<script setup lang="ts">
import { useAuthStore } from "~/store/auth";
const authStore = useAuthStore();
const router = useRouter();
interface loginForm {
email: string;
password: string;
}
let loginForm: loginForm = {
email: "",
password: "",
};
/**
* If success: redirect to home page
* Else display alert error
*/
function login() {
authStore
.login(loginForm)
.then((_response) => router.push("/"))
.catch((error) => console.log("API error", error));
}
</script>
<template>
<v-container>
<form #submit.prevent="login">
<label>E-mail</label>
<input v-model="loginForm.email" required type="email" />
<label>Password</label>
<input v-model="loginForm.password" required type="password" />
<button type="submit">Login</button>
</form>
</v-container>
</template>
For the auth store:
// store/auth.ts
import { defineStore } from 'pinia'
const baseUrl = 'API_URL'
export const useAuthStore = defineStore({
id: 'auth',
state: () => ({
/* Initialize state from local storage to enable user to stay logged in */
user: JSON.parse(localStorage.getItem('user')),
token: JSON.parse(localStorage.getItem('token')),
}),
actions: {
async login(loginForm) {
await $fetch(`${baseUrl}/login`, {
method: 'POST',
body: loginForm
})
.then(response => {
/* Update Pinia state */
this.user = response
this.token = this.user.jwt_token
/* Store user in local storage to keep them logged in between page refreshes */
localStorage.setItem('user', JSON.stringify(this.user))
localStorage.setItem('token', JSON.stringify(this.token))
})
.catch(error => { throw error })
},
logout() {
this.user = null
this.token = null
localStorage.removeItem('user')
localStorage.removeItem('token')
}
}
})
I also use the middleware/auth.global.ts proposed by Nais_One.
And this fetch-wrapper exemple I found here as well to avoid having to add token to every requests: https://jasonwatmore.com/post/2022/05/26/vue-3-pinia-jwt-authentication-tutorial-example and it seems to work perfectly. (I just didn't test yet the handleResponse() method).
Hope it can help others :)
That temporary alternative https://www.npmjs.com/package/#nuxtjs-alt/auth is up to date
And that https://www.npmjs.com/package/nuxtjs-custom-auth and https://www.npmjs.com/package/nuxtjs-custom-http work with Nuxt 3 $fetch and no need to use axios
Recently a new package was released that wraps NextAuth for Nuxt3. This means that it already supports many providers out of the box and may be a good alternative to look into.
You can install it via:
npm i -D #sidebase/nuxt-auth
Then it is pretty simple to add to your projects as you only need to include the module:
export default defineNuxtConfig({
modules: ['#sidebase/nuxt-auth'],
})
And configure at least one provider (like this example with Github):
import GithubProvider from 'next-auth/providers/github'
export default defineNuxtConfig({
modules: ['#sidebase/nuxt-auth'],
auth: {
nextAuth: {
options: {
providers: [GithubProvider({ clientId: 'enter-your-client-id-here', clientSecret: 'enter-your-client-secret-here' })]
}
}
}
})
Afterwards you can then get access to all the user data and signin/signup functions!
If you want to have a look at how this package can be used in a "real world" example, look at the demo repo in which it has been fully integrated:
https://github.com/sidebase/nuxt-auth-example
I hope this package may be of help to you and others!
Stumbling on the same issue for a personal project and what I do is declare a composable importing my authStore which is basically a wrapper over $fetch
Still a newb on Nuxt3 and Vue but it seems to work fine on development, still have to try and deploy it though
import { useAuthStore } from "../store/useAuthStore";
export const authFetch = (url: string, opts?: any | undefined | null) => {
const { jwt } = useAuthStore();
return $fetch(url, {
...(opts ? opts : {}),
headers: {
Authorization:`Bearer ${jwt}`,
},
});
};
And then I can just use it in my actions or components
// #/store/myStore.ts
export const useMyStore = defineStore('myStore', () => {
async getSomething() {
...
return authFetch('/api/something')
}
})
// #components/myComponent.vue
...
<script setup lang="ts">
const handleSomething = () => {
...
authFetch('/api/something')
}
</script>
Hope it helps someone !

Nuxt js <nuxt-link /> authentication

I have a route in nuxt that has to be accessible only by logged in users: /dashboard/secret.
In /dashboard page I have a link like this:
<nuxt-link to="/dashboard/secret">Link to "secret" page</nuxt-link>
When clicked, nuxt will fetch that page from
myapp.com/_nuxt/pages_dashboard_secret.js
How can I add authentication for that nuxt route to stop people from manually going to that url and reading the contents?
Yes the actual secret data will be taken from external api which will validate user token, but still it seems wrong that people can see even the html of this page
if you just want to protect a js file, it would be wrong to do it like this. But if you mean you just want to protect a route from being accessed manually by the users, you must try Nuxt Middlewares and write a middleware for authentication and user fetching.
The middleware structure can be as simple as this:
export default function ({ store, redirect }) {
// If the user is not authenticated
if (!store.state.authenticated) {
return redirect('/login')
}
}
and you can simply use it like this in your root (or secretPage) layout:
<template>
<h1>Secret page</h1>
</template>
<script>
export default {
middleware: 'authenticated'
}
</script>
You can use nuxt/auth package, that is the case for your work and can be used as a plugin and module, you can check has it for the be accessible page or not, it runs automatically and has semantic structure.
You cannot keep your secret on client side (in your JS code) everyone using your application can get it from his browser. You need to keep secret keys on server side and make some validation endpoint to provide if user is valid or not or just keep his session after login.
you can use middleware in nuxt framework. Also, route has a information about url and request. You can make a logic by using them.
https://nuxtjs.org/docs/directory-structure/middleware/
middleware/auth.js
export default async function ({store, from, route, req}) {
if (process.client) {
if (route.name === 'dashboard-room-id' && from.name === route.name)
return
else await store.dispatch('checkSession', route)
}
}
save the token in the store on nuxtServerInit or whenever you get it.
on /dashboard/secret page check in the fetch method if there is a token set.
if token is set, fetch your data otherwise redirect the use somewhere else
https://nuxtjs.org/examples/auth-routes/#redirect-user-if-not-connected
For such a guard of pages, the middleware is the sure way to do it.
Create a middleware file in the middleware directory
Add your middleware logic as described here https://nuxtjs.org/api/pages-middleware/
Then add the middleware option in your page component
as it is mentioned that the routing should be done on the server, in case you just want to handle it if I have this
store/index.js action
async nuxtServerInit({ dispatch, commit }, { req }) {
try {
if (process.server && process.static) { return }
if (!req.headers.cookie) {
console.log('return ')
return
}
const parsed = cookieparser.parse(req.headers.cookie)
const accessTokenCookie = parsed.__session
if (!accessTokenCookie) { return }
const decoded = JWTDecode(accessTokenCookie)
if (userData.exists) {
commit('setState', { name: 'user',
value: {
uid: decoded.user_id,
email: decoded.email,
...userData.data()
} })
}
} catch (e) {
console.log(e)
}
},
//Login firebase
async fireLogin({ dispatch }, { singInWith, account }) {
const resp = await this.$firebase.auth()signInWithEmailAndPassword(account.email, account.password)
const token = await resp.user.getIdToken()
Cookie.set('__session', token)
return { email: resp.user.email, uid: resp.user.uid }
}
Middleware/auth.js
export default function({ store, route, redirect }) {
const user = store.state.user
const blockedRoute = /\/admin\/*/g
const homeRoute = '/'
if (!user && route.path.match(blockedRoute)) {
redirect('/')
}
/*if (user && route.path === homeRoute) {
redirect('/admin')
}*/
}
nuxt.config
router: {
middleware: [
'authenticated'
]
},
you can set the middleware for current page
middle ware
export default context => {
//set Condition and logic
};
route page :
middleware: 'name of middle ware'
i can suggest three solutions:
1.Get pathname in your js codes and then check the url that client using to access your page , for example if pathname is
/dashboard/secret and user is logged in then show the page
for checking pathname u can use these cods:
$nuxt.$route.path
//or good old pure js ;)
window.location.pathname
2.check if user truly logged in (backend & frontend)
for that u can use nuxt-auth and sync it to your backend as well.
for example if you using laravel , u can use laravel passport ,
in that case when the request sended to the backend route, you can check if user is logged in to the backend as well.
Ps:This way is more secure and of course in every backend language this process can be different, but surely all of them will have the same capability.
3.using .htaccess :
Do not allow the user to view the file directly from the server path
Read more

Keycloak Angular 2 - Check authenticated status Keycloak object

I'm implementing the Keycloak authentication service in my Angular 2 project.
I use a service for logging in, logging out etc.
Authenticating a user and logging out seems to work. I'm now trying to protect some routes. I now have a working AuthGuard.
To check if the user is logged in (in the AuthGuard), I have a isAuthenticated() method in the service.
This is the service:
import { Injectable } from '#angular/core';
declare let Keycloak: any;
#Injectable()
export class KeycloakService {
private keycloak = new Keycloak('app/keycloak/keycloak.json');
constructor() {
this.keycloak.init({onload: 'check-sso'});
console.log(this.keycloak);
}
public login() {
this.keycloak.login();
}
public logout() {
this.keycloak.logout();
}
public isAuthenticated() {
return this.keycloak.authenticated;
}
}
Flow: User logs in, user tries to reach protected route, AuthGuard checks if user is logged in via isAuthenticated().
Note: I don't want to authenticate the user for the complete Angular app. Only for some routes.
Problem
After the user logs in, the user is redirected to the Angular app. After this, the isAuthenticated() method returns still false. Here is why:
I logged the Keycloak object to the console. I found something I didn't understand.
Keycloak object after login redirect
Same Keycloak object after login redirect (but expanded)
First the authenticated property is false. After expanding the authenticated property is true.
Question
Is the way I try to maintain my Keycloak object the correct way?
Consulted sources
https://keycloak.gitbooks.io/securing-client-applications-guide/content/v/2.5/topics/oidc/javascript-adapter.html
https://github.com/keycloak/keycloak/tree/master/examples/demo-template/angular2-product-app/src/main/webapp/app
And others
Basing on the community provided Angular2 example in keycloak's github you can spot some differences in interacting with keycloak js adapter.
Mainly the actual check on the authenticated (and possibly userName) is done on the promise returned from init.
static init(): Promise<any> {
let keycloakAuth: any = new Keycloak('keycloak.json');
KeycloakService.auth.loggedIn = false;
return new Promise((resolve, reject) => {
keycloakAuth.init({ onLoad: 'login-required' })
.success(() => {
KeycloakService.auth.loggedIn = true;
KeycloakService.auth.authz = keycloakAuth;
KeycloakService.auth.logoutUrl = keycloakAuth.authServerUrl + "/realms/demo/protocol/openid-connect/logout?redirect_uri=/angular2-product/index.html";
resolve();
})
.error(() => {
reject();
});
});
}
Also the official keycloak js adapter's documentation uses promise for the authenticated check
<head>
<script src="keycloak.js"></script>
<script>
var keycloak = Keycloak();
keycloak.init().success(function(authenticated) {
alert(authenticated ? 'authenticated' : 'not authenticated');
}).error(function() {
alert('failed to initialize');
});
</script>
</head>
If you use check-sso as a parameter to init function, the browser will be routed back to the application if the user is not logged in and will remain unauthenticated.You should use login-required instead to fix this problem.
If you don't want to authenticate the user for the complete App, you should detach the logic of creating the adapter, to make things easier if you have more than one secured component. for exemple you can create a HOC.
PS : in the example below, I am using Reactjs, I hope you can find a similar way to do this in angular:
export default (WrappedComponent) => {
return (props) => {
const [isAutenticated, setIsAutenticated] = useState(false);
const [keycloak, setKeycloak] = useState();
const loadConfig = useCallback(() => {
const keycloak = Keycloak("/keycloak.json"); //The configuration of the adapter in JSON format
keycloak.init({ onLoad: "login-required" }).then((authenticated) => {
setKeycloak(keycloak);
setIsAutenticated(authenticated);
});
}, [Keycloak]);
useEffect(() => {
loadConfig();
}, [loadConfig]);
if (keycloak) {
if (isAutenticated) {
return <WrappedComponent {...props} keycloak={keycloak} />;
} else return <AuthError message="Unable to authenticate" />;
}
return <Loader />;
};
};
you can find a useful source here

Auth0 callback URL mismatch

I am doing LinkedIn authentication with auth0 in a react app. I have set localhost:3000/upload in callback urls in settings, hopping that after users login at localhost:3000/login, they would be redirected to localhost:3000/upload. However, I always get this error: url localhost:3000/login is not in the list of callback urls. Why would auth0 expect to return to the page where you just logged in after logging in. Shouldn't it be some different url. It just does not make sense to me.
Edit:
export default class AuthService {
constructor(clientId, domain) {
// Configure Auth0
const options = {
allowedConnections: ['linkedin'],
auth: {
params: {responseType: 'code'}
}
};
this.lock = new Auth0Lock(clientId, domain, options)
// Add callback for lock `authenticated` event
this.lock.on('authenticated', this._doAuthentication.bind(this))
// binds login functions to keep this context
this.login = this.login.bind(this)
this.loggedIn = this.loggedIn.bind(this)
}
_doAuthentication(authResult){
// Saves the user token
console.log(authResult);
this.setToken(authResult.idToken)
this.lock.getProfile(authResult.idToken, (error, profile) => {
if (error) {
console.log('Error loading the Profile', error)
} else {
console.log(profile)
}
})
}
//....
Please ensure two things:
1). In your react app code
responseType: 'code'
2). On the Auth0 dashboard, under Settings -> Allowed Callback URLs put your callback entry (localhost:3000/upload) - which I think you have done but just in case.
Let me know if you are still having problems.
Make sure that there is no special hidden characters or space between the commas between the URLs when you paste it into the Auth0 Setting site. I didn't realise about this util I put every urls into Vim to check and see that there are such above cases
In the call to AuthProvider, make sure to use to same callback url as the one in Auth0 settings:
const uri='http://localhost:3000/upload';
<Auth0Provider
domain={domain}
clientId={clientId}
redirectUri={uri}>
To cause a redirect to a different URL after a successful authentication, you need to provide the redirectUrl to Lock, like this:
// Configure Auth0
const options = {
allowedConnections: ['linkedin'],
auth: {
responseType: 'code',
redirectUrl: 'http://localhost:3000/upload'
}
};
this.lock = new Auth0Lock(clientId, domain, options)
(Also notice that the responseType option goes under auth, not under auth.params.)
If you do the redirect, you won't reach the event handler you defined in your login page. You will need to either add an event handler in your destination page (and use responseType:token) or handle authentication results in your server code (this is what you will normally be doing if you are requesting a responseType: code).
the reason why you should set the callback Url in auth0 settings, because any one can use your client id and send request to google or linkedin, get the response to anywhere they set. but with this setting only you can access that response.
once your app is authorized to pull the data from linkedin, linkedin will send the data to where you specified. you should create a page to handle the response from Linkedin server. Let's name that page callback.js and this will be an example of response object.
accessToken: "hNuPLKTZHiE9_lnED0JIiiPNjlicRDp"
appState: null
expiresIn: 7200
idToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRXdSVUl5TURVeE4wSkJPRFZEUlRKRU1EVkZNemsxTXpNNU5VTXlNRGt6T0VWQlJqUkZRUSJ9.eyJodHRwOi8vbG9jYWxob3N0OjMwMDAvcm9sZSI6InNpdGVPd25lciIsImdpdmVuX25hbWUiOiJvbWFyIiwiZmFtaWx5X25hbWUiOiJpYm8iLCJuaWNrbmFtZSI6Im9tYXJpYm8xOTgyIiwibmFtZSI6Im9tYXIgaWJvIiwicGljdHVyZSI6Imh0dHBzOi8vbGg1Lmdvb2dsZXVzZXJjb250BQUFBQUkvQUFBQUFBQUFBQUEvQUNIaTNyLTEwLTEyVDIyOjU4OjAxLjgzM1oiLCJpc3MiOiJodHRwczovL3BvcnRmb2xpby15aWxtYXouYXV0aDAuY29tLyIsInN1YiI6Imdvb2dsZS1vYXV0aDJ8MTE0MDY0NTA2ODI2OTgwNTA5ODY3IiwiYXVkIjoiUEdVY242RjRRS21PRkJhb1k0UFdCeWpjVzIyT09vNGMiLCJpYXQiOjE1NzA5MjEwODIsImV4cCI6MTU3MDk1NzA4MiwiYXRfaGFzaCI6InN0R1l5SnJaMHNnbVYzSWNLWjlPeFEiLCJub25jZSI6InRrOV95b096enRmVThVVjFVMlVFR3IyMW5ORW5abjk4In0.TYS7mM8N2d7jEHFdWQGTSeAAUaDt4-0SMUG3LrcQ1r3xzY0RMGsUsEszj5xqk1GE0cIlFS10xCOYKsuHSwsFLomC1EbLjntjkledHtfD0MW84cMoXN6a-x-1-bNwl3lMYJ98qklTrNvTvkQJ6DWhei3hJ8rs8dnbNyCfckNVU6ptJU-9ef1DwWfHRomW5LQ6WSDRHZScW697gdgBEMU-Nd2SddyHhQe0kVh6lKdcbnskEAyCJLE07jfM40RQI_8LJouFcpoyImcXSDZlKv90tYfVDq9_TwE3GNaSz5I5snn0457oCgz0vuX0JoCUiaDuTIX7XiyXnozW_DxGMuhk4w"
idTokenPayload: {http://localhost:3000/role: "siteOwner", given_name: "me", family_name: "you", nickname: "nck", name: "nm", …}
refreshToken: null
scope: null
state: "xkEbffzXbdOYPLkXOUkrQeb0Jysbnlfy"
tokenType: "Bearer"
//THIS CODE IS FOR NEXT.JS9
//auth.js
class Auth0 {
constructor() {
this.auth0 = new auth0.WebAuth({
domain: "portfolio-ys.auth0.com",
clientID: "PGUWJQKmOFBaoY4PWByjcW22OOo4c",
redirectUri: "http://localhost:3000/callback",
responseType: "token id_token",
scope: "openid profile"
});
this.handleAuthentication = this.handleAuthentication.bind(this);
}
//there are too many methods are defined here i put only relevant ones
handleAuthentication() {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
console.log(authResult);
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult);
resolve();
} else if (err) {
reject(err);
}
});
});
}
setSession function is where you set the cookies based on response object. I use js-cookie package to set the cookie.
setSession(authResult) {
const expiresAt = JSON.stringify(
authResult.expiresIn * 1000 + new Date().getTime()
);
Cookies.set("user", authResult.idTokenPayload);
Cookies.set("jwt", authResult.idToken);
Cookies.set("expiresAt", expiresAt);
}
}
const auth0Client = new Auth0();
export default auth0Client;
callback.js
import React from "react"
import auth0Client from "./auth0"
import {withRouter} from "next/router"
class Callback extends React.Component{
async componentDidMount(){
await auth0Client.handleAuthentication()
this.props.router.push('/')
}
render() {
return (
<h1>verifying logging data</h1>
)
}
}
export default withRouter(Callback) //this allows us to use router
I had similar issue "callback URL mismatch" and resolved it by running the application over https with a trusted certificate.
Here is a snippet from Auth0 applications settings section about callback URL, which says "Make sure to specify the protocol (https://) otherwisw the callback may fail in some cases."
If you're using the Android(Kotlin) SDK of auth0, I noticed that during runtime, the requested URL is being changed. e.g. app://{your_auth0_domain}/android/{package_name}/callback://{your_auth0_domain}/android/app://{your_auth0_domain}/android//callback
Originally URL was
app://{your_auth0_domain}/android/{package_name}/callback
and SDK is appending "://{your_auth0_domain}/android/app://{your_auth0_domain}/android//callback" this extra part.
Solution: Either put the same URL in auth0 setting dashboard as it showing in your logs
or
WebAuthProvider
.login(account)
.withScheme("app") // instead of complete URL, put only the remaining part from the URL,
.start(this, object : Callback<Credentials, AuthenticationException> {}
I hope it will definitely help android/app developer.

Ember simple-auth mixins deprected

I am an experienced (55+ years) programmer but a total noob in ember and js. I'm trying to get a simple authentication page working using the ember-cli addons ember-cli-simple-auth, ember-cli-simple-auth-oauth2 and cut-and-paste from the simplelabs tutorial.
I get the following in the console:
DEPRECATION: The LoginControllerMixin is deprecated. Use the session's authenticate method directly instead.
and:
DEPRECATION: The AuthenticationControllerMixin is deprecated. Use the session's authenticate method directly instead.
The solution may be trivial, but I have been chasing it for hours and get deep into javascript before reaching a dead-end. The code that is causing these errors is:
import LoginControllerMixin from 'simple-auth/mixins/login-controller-mixin';
export default Ember.Controller.extend(LoginControllerMixin, {
authenticator: 'simple-auth-authenticator:oauth2-password-grant'
});
which invokes the ApplicationControllerMixin somewhere in the bower code.
Before I "re-invent the wheel" by translating some old html/ruby/pascal code into js, can anyone help me "Use the session's authenticate method directly instead."?
Thanks.
I feel you're pain. I spent weeks trying to sort this out. A big part of the problem is that so much has changed in the past couple of years and there are a lot of code examples out there that are outdated or don't work together. It's really difficult to put the various pieces together coherently, and figure out what one does NOT need to do.
That said, please keep in mind that i'm a n00b as well. What i've done seems to work ok but i've no idea whether there's a much better way.
Also, what you're trying to do may not be the same as what i've done. My app authenticates against google (and twitter, fb, etc.) using Simple-Auth-Torii and then exchanges the returned Authenication Code for an Authentication Token. That last part happens on the server. So, after the session authenticates, i then pass the auth code to the server and get back the auth code.
// routes/login.js
import Ember from "ember";
import ENV from "../config/environment";
export default Ember.Route.extend({
setupController: function(controller, model) {
controller.set("errorMessage", null);
},
actions: {
googleLogin: function() {
var _this = this;
// Using "session's authenticate method directly" right here
this.get("session").authenticate("simple-auth-authenticator:torii", "google-oauth2")
.then(function() {
// We're now authenticated. The session should now contain authorization
// code from provider. We now need to exchange that for an auth token.
var secureData = _this.get("session.content.secure");
// call server to initiate token exchange from provider
var exchangeData = {
authorizationCode: secureData.authorizationCode,
redirectUri : secureData.redirectUri,
provider : 'google'
};
// sends ajax request to server, which will in turn call provider
// with authentication code and receive auth token
_this.tokenService.fetch(exchangeData).then(function(response) {
if (response.success) {
_this.set("session.content.secure.access_token", response.data.token);
_this.set("session.content.secure.userData", response.data.user);
// take user somewhere ...
_this.transitionTo("data_sets");
}
else {
// set an error message, log the response to console, whatever, but
// we need to invalidate session because as far as simple-auth
// is concerned we're already authenticated. The following logs the user out.
_this.get("session").invalidate();
}
}, function(error) {
console.log("tokenService.fetch error", error);
_this.get("session").invalidate();
});
}, function(error) {
console.log("simple-auth-authenticator:torii error", error);
_this.get("session").invalidate();
});
},
twitterLogin: function() {
// etc.
}
}
});
Logging a user out also uses the session directly.
{{!templates/application.hbs}}
<ul class="nav navbar-nav navbar-right">
{{#if session.isAuthenticated}}
<li><button {{ action 'invalidateSession' }} class="btn btn-sm">Logout</button></li>
{{/if}}
...
</ul>
// routes/application.js
import Ember from "ember";
import ENV from "../config/environment";
import ApplicationRouteMixin from "simple-auth/mixins/application-route-mixin";
export default Ember.Route.extend(ApplicationRouteMixin, {
actions: {
// action is globally available because in application route
invalidateSession: function() {
// the most basic logout
this.get("session").invalidate();
return;
// If you need to invalidate also on the server do something like:
//
// var _this = this;
// return new Ember.RSVP.Promise(function(resolve, reject) {
// var params = {
// url : ENV.logoutEndpoint,
// type : "POST",
// dataType : "json"
// };
// Ember.$.ajax(params).then(function(response) {
// console.log('session invalidated!');
// console.dir(response);
// _this.get("session").invalidate();
// });
// });
}
}
});
I've met the same deprecation problem. I thinks this snippet for a lovely login controller will do, it's a bit more what you asked, but I hope it is still understandable. I use it with devise, it's almost the same except I use it this way: authenticate('simple-auth-authenticator:devise', credentials)
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
authenticate: function() {
// identification and password are the names of the input fields in the template
var credentials = this.getProperties('identification', 'password');
if (!credentials.identification || !credentials.password) {
return false;
}
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', credentials).then(function() {
// authentication was successful
}, function(errorMessage) {
// authentication failed
});
}
}
});