I'm having trouble with the aurelia-auth plugin. I can request a token from my back-end and get a successful response - however, my app doesn't appear to be authenticated and no token is saved to local storage.
auth-config.js
var config = {
baseUrl: 'http://localhost:64794',
signupUrl: 'users',
loginUrl: 'api/values/PostPassword',
tokenName: 'id_token',
loginRedirect: '#/welcome',
authHeader: 'Authorization',
authToken: 'Bearer',
storage: 'localStorage'
}
export default config;
login.js
import {AuthService} from 'aurelia-auth';
import {inject} from 'aurelia-framework';
#inject(AuthService)
export class Login {
heading = 'Login';
email = '';
password = '';
loginError = '';
constructor(auth) {
this.auth = auth;
};
login() {
return this.auth.login(this.email, this.password)
.then(response => {
console.log("Login response: " + response);
console.log("Auth: " + this.auth.isAuthenticated());
})
.catch(error => {
this.loginError = error.response;
});
};
}
Console output:
Login response: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImIiLCJuYmYiOjE1MDkyNzkxMzYsImV4cCI6MTUwOTI4MDMzNiwiaWF0IjoxNTA5Mjc5MTM2fQ.4QZu8pQI-K_71x_CKT9ANu1vQD7VvVUcyep51CvvCXg
login.js:27 Auth: false
Any advice would be appreciated.
token (or the object containing it) is expected to be the value of prop 'access_token' in the response by default in aurelia-auth
so you need to change the structure of your api response to
{ access_token: YOUR_TOKEN }
if you have no control over the server, you could still extract the token by overriding the default config. one in question here is responseTokenProp, more here
also, for a clearer picture as to how the token is extracted, have a look here
Related
I am controlling user authentification in my next app with next-auth library
I am using the credentials provider. First I call the login endpoint which returns the user informations then I take the access token and put it inside the token given by next-auth callback.
this is my code in [...nextauth].js
const authOptions = {
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
type: "credentials",
credentials: {},
async authorize(credentials, req) {
const { email, password } = credentials;
const result = await axios.post(
`http://127.0.0.1:5000/user/login`,
{
email,
password,
},
{
headers: { "Content-Type": "application/json" },
withCredentials: true,
}
);
return {
accessToken: result.data.accessToken,
};
},
}),
],
callbacks: {
async jwt({ user, token }) {
if (user?.accessToken) {
token.value = user.accessToken;
}
console.log(token); //<-- output below
return token;
},
},
};
output :
{
value: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYzOTZiMTlhYTczMmUzMzYwMjU2ZjBlMiIsImlhdCI6MTY3NTAyMzEwNSwiZXhwIjoxNjc1MTA5NTA1fQ.5kdPmeLCpwbJBjtzKMhe5QMNEx75ThiDKm75PN0vjoc',
iat: 1675023106,
exp: 1675109506,
jti: 'd9108700-1b5f-4bd3-8d31-0c36f38d9fcb'
}
Now in getServerSideProps I can get it from the request because it is sent in Cookie
export async function getServerSideProps(context) {
console.log(context.req.cookies["next-auth.session-token"]); // <-- output in Blockquote
return {
// does not matter
};
}
I get this :
eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..6ryJ60GPcDLq9aWG.4oWlJbecyWUnbZYJiv6z0eAuFmRFSfEn4fQSlh1FTjlPiiDGZASA4UwqXNEHRpRMG6HRPRDcsUUCHBBzaV8JwCEetgSYJcSrZ5CK_AhyvFKUlKY-TpHSNDnmCI8ZS4y2nV_Xl0NqvMU3vA-D8gXtT5UcOrJLlN5dMe7S9xZo8vhr-gpohcEhKOefUgDjTmMYmBf190OLl0TY599FkJwpoeSFozAwavwbOZGQOxYVbsj3KTibsfE37juyqnDaiV_t59bWroGjz2d5kHLxfkpQB0IKYRnAH8sXbG7dDZUVLT1UQUN_FrjYpkFrQgxC7MmWZtCccQs-FsBXY7EbiYmJKIddpOeN1Q.1kas8bGE_O7IkEDiilxiZw
Now I want to decrypt this token to get its property 'value' (which is the accessToken) and use it.
is it possible to decrypt it with javascript ? Thank you for your attention !
this funciton is inside next-auth/jwt module:
async function decode(params) {
const {
token,
secret
} = params;
if (!token) return null;
const encryptionSecret = await getDerivedEncryptionKey(secret);
const {
payload
} = await (0, _jose.jwtDecrypt)(token, encryptionSecret, {
clockTolerance: 15
});
return payload;
}
since this is not exported you have to export all module
import jwt from "next-auth/jwt"
// since you reached the token inside getServerSidePrps passed here
jwt.decode(token,passYourSecret)
May jwt.decode(token,secret) works as montioned in #Yilmaz answer but there is a built-in helper method getToken() for doing that
For convenience, this helper function is also able to read and decode tokens passed from the Authorization: 'Bearer token' HTTP header.
import { getToken } from "next-auth/jwt";
const secret = 'MY_SECRET';
export default async function handler(req, res) {
const token = await getToken({ req, secret })
console.log("JSON Web Token", token)
res.end()
}
If using NEXTAUTH_SECRET env variable, we detect it, and you won't actually need to secret
export default async function handler(req, res) {
const token = await getToken({ req })
console.log("JSON Web Token", token)
res.end()
}
Im using Auth0 to authenticate users.
Im protected api routes like this:
// pages/api/secret.js
import { withApiAuthRequired, getSession } from '#auth0/nextjs-auth0';
export default withApiAuthRequired(function ProtectedRoute(req, res) {
const session = getSession(req, res);
const data = { test: 'test' };
res.json({ data });
});
My problem is when I'm trying to fetch the data from getServerSideProps I'm getting 401 error code.
If I use useEffect Im able to get data from api route.
Im trying to fetch the data like this:
export const getServerSideProps = withPageAuthRequired({
async getServerSideProps(ctx) {
const res = await fetch('http://localhost:3000/api/secret');
const data = await res.json();
return { props: { data } };
},
});
Im getting the following response:
error: "not_authenticated", description: "The user does not have an active session or is not authenticated"
Any idea guys? Thanks!!
When you call from getServerSideProps the protected API end-point you are not passing any user's context (such as Cookies) to the request, therefore, you are not authenticated.
When you call from useEffect it runs inside your browser, which attaches all cookies to the request, one of them is the session cookie.
You need to forward the session cookie that was passed to the getServerSideProps (by the browser) to the API call.
export const getServerSideProps = withPageAuthRequired({
async getServerSideProps(ctx) {
const res = await fetch('http://localhost:3000/api/secret', {
headers: { Cookie: ctx.req.headers.cookie },
// ---------------------------^ this req is the browser request to the getServersideProps
});
const data = await res.json();
return { props: { data } };
},
});
For more info.
#auth0/nextjs-auth0 has useUser hook. This example is from: https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/
// pages/index.js
import { useUser } from '#auth0/nextjs-auth0';
export default () => {
const { user, error, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (user) {
return (
<div>
Welcome {user.name}! Logout
</div>
);
}
// if not user
return Login;
};
Note that authentication takes place on the server in this model,
meaning that the client isn't aware that the user is logged in. The
useUser hook makes it aware by accessing that information in the
initial state or through the /api/auth/profile endpoint, but it won't
expose any id_token or access_token to the client. That information
remains on the server side.
Custom HOF:
// getData is a callback function
export const withAuth = (getData) => async ({req, res}) => {
const session = await auth0.getSession(req);
if (!session || !session.user) {
res.writeHead(302, {
Location: '/api/v1/login'
});
res.end();
return {props: {}};
}
const data = getData ? await getData({req, res}, session.user) : {};
return {props: {user: session.user, ...data}}
}
Example of using:
export const getServerSideProps = withAuth(async ({req, res}, user) => {
const title = await getTitle();
return title;
});
I created a Nuxt app that uses Django on the backend, i'm using the standard Django Session Authentication, so when i log in from Nuxt, a session cookie is set in my browser.
I've been trying for days to find a way to restrict some pages to authenticated users only, but i don't seem to find any working approach to do that. I need to check if the user is logged in before the page is loaded, so i tried to use a middleware but middleware won't work at all because the middleware is executed from server side (not client side) so there won't be any cookie in the request.
At this point, is there any other way to do this from SSR? Here is my request:
export default async function (context) {
axios.defaults.withCredentials = true;
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//Check if user is authenticated - response is always False
}).catch(function (error) {
//Handle error
});
}
If you are running Nuxt in SSR mode as server, you can access the cookie headers to find out if the user has a certain cookie. Packages like cookieparser (NPM) can easily do that for you.
But as you already found out, you can't do that in a middleware. What you could use instead is the nuxtServerInit action in your store (Docs). This action is run on the server and before any middleware gets executed. In there you can use cookieparser to get the user's cookies, authenticate them and save the any information you need in the store.
Later you can access the store in your middleware and for example redirect the user.
actually you can get cookies in a middleware.... Ill put my example, but the answer above is more correct .
middleware/auth.js
import * as cookiesUtils from '~/utils/cookies'
export default function ({ route, req, redirect }) {
const isClient = process.client
const isServer = process.server
const getItem = (item) => {
// On server
if (isServer) {
const cookies = cookiesUtils.getcookiesInServer(req)
return cookies[item] || false
}
// On client
if (isClient) {
return cookiesUtils.getcookiesInClient(item)
}
}
const token = getItem('token')
const { timeAuthorized } = cookiesUtils.authorizeProps(token)
const setRedirect = (routeName, query) => {
return redirect({
name: routeName,
query: query
? {
redirect: route.fullPath
}
: null
})
}
// strange bug.. nuxt cant redirect '/' to '/login'
if (route.path === '/') {
setRedirect('users')
}
if (!route.path.match(/\/login\/*/g) && !timeAuthorized) {
setRedirect('login', true)
}
}
utils/cookies.js
import Cookie from 'js-cookie'
import jwtDecoded from 'jwt-decode'
/*
TOKEN
*/
// Get server cookie
export const getcookiesInServer = (req) => {
const serviceCookie = {}
if (req && req.headers.cookie) {
req.headers.cookie.split(';').forEach((val) => {
const parts = val.split('=')
serviceCookie[parts[0].trim()] = (parts[1] || '').trim()
})
}
return serviceCookie
}
// Get the client cookie
export const getcookiesInClient = (key) => {
return Cookie.get(key) || false
}
export const setcookiesToken = (token) => {
Cookie.set('token', token)
}
export const removecookiesToken = () => {
Cookie.remove('token')
}
export const authorizeProps = (token) => {
const decodeToken = token && jwtDecoded(token)
const timeAuthorized = (decodeToken.exp > Date.now() / 1000) || false
return {
timeAuthorized
}
}
I have a SPA which uses the solution provided here to authenticate with Azure AD and everything works as expected. Now I want to migrate this to use MSAL.js.
I use below for login:
import * as MSAL from 'msal'
...
const config = {
auth: {
tenantId: '<mytenant>.com',
clientId: '<myclientid>',
redirectUri: <redirecturi>,
},
cache: {
cacheLocation: 'localStorage',
}
};
const tokenRequest = {
scopes: ["User.Read"]
};
export default {
userAgentApplication: null,
/**
* #return {Promise}
*/
initialize() {
let redirectUri = config.auth.redirectUri;
// create UserAgentApplication instance
this.userAgentApplication = new MSAL.UserAgentApplication(
config.auth.clientId,
'',
() => {
// callback for login redirect
},
{
redirectUri
}
);
// return promise
return new Promise((resolve, reject) => {
if (this.userAgentApplication.isCallback(window.location.hash) || window.self !== window.top) {
// redirect to the location specified in the url params.
}
else {
// try pull the user out of local storage
let user = this.userAgentApplication.getUser();
if (user) {
resolve();
}
else {
// no user at all - go sign in.
this.signIn();
}
}
});
},
signIn() {
this.userAgentApplication.loginRedirect(tokenRequest.scopes);
},
And then I use below to get the token:
getCachedToken() {
var token = this.userAgentApplication.acquireTokenSilent(tokenRequest.scopes);
return token;
}
isAuthenticated() {
// getCachedToken will only return a valid, non-expired token.
var user = this.userAgentApplication.getUser();
if (user) {
// get token
this.getCachedToken()
.then(token => {
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
// get current user email
axios
.get('<azureapi-endpoint>' + '/GetCurrentUserEmail')
.then(response => { })
.catch(err => { })
.finally(() => {
});
})
.catch(err => { })
.finally(() => { });
return true;
}
else {
return false;
}
},
}
but after login I get below error:
Access to XMLHttpRequest at 'https://login.windows.net/common/oauth2/authorize?response_type=code+id_token&redirect_uri=<encoded-stuff>' (redirected from '<my-azure-api-endpoint>') from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Also the token that I get seems to be invalid as I get 401 errors trying to call api using the token. Upon checking the token against https://jwt.io/ I get an invalid signature.
I really appreciate anyone's input as I've already spent good few days and haven't got anywhere yet.
I'm not sure if this is your issue. however, for msal.js, in the config, there is no tenantId parameter, it's supposed to be authority. Here is a sample for graph api using msal.js
https://github.com/Azure-Samples/active-directory-javascript-graphapi-v2
specifically: the config is here: https://github.com/Azure-Samples/active-directory-javascript-graphapi-v2/blob/quickstart/JavaScriptSPA/authConfig.js
as per here, https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-js-initializing-client-applications it is supposed to be hitting login.microsoftonline.com not login.windows.net
I have a SPA that is built on vuejs. When a user is logged in via API, the token is stored in local storage.
I need a global solution which will logout and prompt the user when the token is no longer valid. At the moment, I get "invalid token" error when accessing private API endpoints.
How do I rig axios so that ALL response of invalid tokens will trigger the logout/prompt code?
Here is an simple example with axios. It use a Bearer token for authentification.
import axios from "axios";
import { useUserStore } from "#/store/userStore";
const apiClient = axios.create({
baseURL: ""http://127.0.0.1:8001",
headers: {},
});
apiClient.interceptors.response.use(
(response) => response,
async (error) => {
const config = error?.config;
if (error?.response?.status === 401) {
const result = await refreshToken();
if (result) {
config.headers = {
...config.headers,
authorization: `Bearer ${result?.token}`,
};
}
return axios(config);
}
);
const refreshToken = async () => {
/* do stuff for refresh token */
// if refresh token failed
try {
useUserStore().actionLogout();
} catch (error) {
console.log(error);
} finally {
loacalStorage.clear();
}
};
you can write a function that clears your local storage after some time and logout user