I'm using next-auth to authenticate users in a NextJS app, when authenticated the express api sets a token cookie that I want to use later for api routes protection. Now the authentication works, but the cookie doesn't get saved in the cookie storage in the browser although it does on Postman.
here's my credentials provider code
CredentialsProvider({
async authorize(credentials) {
try {
const {data} = await axios.post(`${process.env.URL}/api/v1/auth/login`, {
email: credentials.email,
password: credentials.password,
}, {
headers: {
'Content-Type': 'application/json'
}
})
if (data) {
return {
username: data.username,
email: data.email,
}
} else {
return null
}
} catch (error) {
return null
}
}
})
Related
I have created admin dashboard for one client. Project is created using Vue.js with Nuxt.js. Backend is Directus and it was created by my colleague.
Problem is that auth middleware is not working as I need.
When I log in, I save AUTH_TOKEN and REFRESH_TOKEN in cookies. Then, I am firing up one API call, if response.message is: 'Token expired', I send new API call with REFRESH_TOKEN to refresh. Then, from response I save new REFRESH_TOKEN and new AUTH_TOKEN to cookies again and if response is not 200, I redirect user to /login.
Here is my code (/middleware/authenticated.js):
import authService from '../services/authService';
export default function ({ $cookies, redirect, store, $toast, $router }) {
const access_token = $cookies.get('access_token');
const refresh_token = $cookies.get('refresh_token');
if (!access_token) {
$cookies.remove('access_token');
$cookies.remove('refresh_token');
store.commit('RESET_USER');
return redirect('/login');
}
if(access_token){
store.commit('SET_USER');
setInterval(function(){
try{
authService.retrieveCurrentUser({headers: {
"Content-type": "application/json",
"Authorization": `Bearer ${access_token}`,
}})
.then(() => {
console.log('ok');
})
.catch ((error) => {
console.log('prvy catch error', error);
if(error.response.data.errors[0].message == 'Token expired.'){
const config = {
"refresh_token": refresh_token
}
authService.refreshToken(config)
.then((response) => {
$cookies.set('access_token', response.data.data.access_token);
$cookies.set('refresh_token', response.data.data.refresh_token);
store.commit('SET_USER');
return redirect();
})
.catch((err) => {
console.log('druhy catch error', err);
$cookies.remove('access_token');
$cookies.remove('refresh_token');
store.commit('RESET_USER');
$toast.error('Platnosť vášho prihlásenia vypršala, prihláste sa prosím znova.', { timeout: 5000 });
clearInterval(this);
return redirect('/login');
})
}
})
}
catch (err){
console.log('treti catch error', err);
$cookies.remove('access_token');
$cookies.remove('refresh_token');
store.commit('RESET_USER');
$toast.error('Platnosť vášho prihlásenia vypršala, prihláste sa prosím znova.', { timeout: 5000 });
clearInterval(this);
return redirect('/login');
}
}, 300000)
}
};
Here is authService:
import api from '#/services/api';
export default {
login (credentials){
return api().post('/auth/login', credentials);
},
refreshToken(config) {
return api().post('/auth/refresh', config);
},
logout (refresh_token){
return api().post('/auth/logout', refresh_token);
},
retrieveCurrentUser(refresh_token){
return api().get('/users/me', refresh_token);
}
};
And this is how I call middleware inside page:
middleware: 'authenticated',
Also I need that setInterval because I want to check if token is still valid every 5 minutes.
When I use this code, I am receiving automatic log outs, or spamming of that toast notification.
Hi everyone!
I have my own custom strategy to get token, and all is good, but when a refresh page I lose user data and fetchUser does not works. It doesn´t send the params to API to get again the user data.
the workflow is next:
1- send params to token api and get token
2- send params to login API to get the user
//nuxt.config.js
customStrategy: {
_scheme: '~/schemes/customScheme',
endpoints: {
login: {
url: '/api/v1/token',
method: 'post',
propertyName: 'token',
headers: {'x-channel-id': 1}
},
user: {
url: '/api/v1/login',
method: 'post',
propertyName: false,
headers: {'x-channel-id': 1}
},
logout: null
}
}
customScheme.js
import LocalScheme from '#nuxtjs/auth/lib/schemes/local'
export default class CustomScheme extends LocalScheme {
_setToken (token) {
if (this.options.globalToken) {
// Set Authorization token for all axios requests
this.$auth.ctx.app.$axios.setHeader(this.options.tokenName, token)
}
}
_clearToken () {
if (this.options.globalToken) {
// Clear Authorization token for all axios requests
this.$auth.ctx.app.$axios.setHeader(this.options.tokenName, false)
}
}
mounted () {
if (this.options.tokenRequired) {
const token = this.$auth.syncToken(this.name)
this._setToken(token)
}
return this.$auth.fetchUserOnce()
}
async login (endpoint) {
if (!this.options.endpoints.login) {
return
}
// Get token
const result = await this.$auth.request({
...endpoint
},
this.options.endpoints.login
)
// Set token
if (this.options.tokenRequired) {
const token = this.options.tokenType
? this.options.tokenType + ' ' + result
: result
this.$auth.setToken(this.name, token)
this._setToken(token)
}
// If result I get and set user
if (result) {
const user = await this.$auth.request({
...endpoint
},
this.options.endpoints.user
)
this.$auth.setUser(user);
}
}
async fetchUser (endpoint) {
// User endpoint is disabled.
if (!this.options.endpoints.user) {
this.$auth.setUser({})
return
}
// Token is required but not available
if (this.options.tokenRequired && !this.$auth.getToken(this.name)) {
return
}
// Try to fetch user and then set
try{
const user = await this.$auth.requestWith(
this.name,
endpoint,
this.options.endpoints.login
)
this.$auth.setUser(user)
} catch (error){
console.log(error)
}
}
}
When I set this.$auth.setUser(user) in login() method all is fine and app redirect me to /dashboard page and the user information (like role and email) is displayed on navBar but when I refresh page I lose user data. The app try to fetchUser but it give me a 400 error because user and password not sent.
Another thing I don´t understand is Why endpoint parameter is undefined in async fetchUser (endpoint) ??? . I think there is an issue in this part.
I hope u can help me
Regards
I just remove all this library and did my own custom Nuxt authentication
https://nemanjadragun92.medium.com/nuxt-js-custom-authentication-245d2816c2f3
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 am new to vue and stuck on this problem for quite some time. I have a login method that retrieves an API token and stores it in localStorage. The login API call is the only call that does not send Auth headers. After the Login every call should add the API token to the header.
When I login the interceptor does not set the new header. It needs a page refresh in the browser to work. Why is that, what am I doing wrong?
In my Login component I have this method:
methods: {
login() {
api.post('auth/login', {
email: this.email,
password: this.password
})
.then(response => {
store.commit('LOGIN');
localStorage.setItem('api_token', response.data.api_token);
});
this.$router.push('reservations')
}
}
Additionally I have this axios base instance and an interceptor:
export const api = axios.create({
baseURL: 'http://backend.local/api/',
// headers: {
// 'Authorization': 'Bearer ' + localStorage.getItem('api_token')
// },
validateStatus: function (status) {
if (status == 401) {
router.push('/login');
} else {
return status;
}
}
});
api.interceptors.request.use((config) => {
config.headers.Authorization = 'Bearer ' + localStorage.getItem('api_token');
return config;
}, (error) => {
return Promise.reject(error);
});
Using Vue webpack template, trying to make JWT authentication. What I've done so far:
"src/auth/index.js":
// Send a request to the login URL and save the returned JWT
login (creds, redirect) {
axios.post(LOGIN_URL, creds, (data) => {
localStorage.setItem('access_token', data.access_token)
this.user.authenticated = true
// Redirect to a specified route
if (redirect) {
router.push(redirect)
}
}).error((err) => {
context.error = err
})
},
I'm calling this function from LoginPage.vue:
methods: {
login () {
var credentials = {
username: this.credentials.username,
password: this.credentials.password
}
// We need to pass the component's this context
// to properly make use of http in the auth service
auth.login(this, credentials, 'requests')
}
}
When I'm submitting the form, data is submitted, but I get the following error in a console:
TypeError: __WEBPACK_IMPORTED_MODULE_1_axios___default.a.post(...).error is not a function
Also JWT token is not saving in my local storage, what am I doing wrong?
Rewrote login function:
login (context, creds, redirect) {
axios.post(LOGIN_URL, creds)
.then((response) => {
localStorage.setItem('access_token', response.data.access_token)
this.user.authenticated = true
if (redirect) {
router.push(redirect)
}
}).catch((err) => {
context.error = err.response.data
})
},
Everything is working now.