JWT authentication with AXIOS - authentication

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.

Related

Next.js How to protect my website routes to authenticated users with NextAuth

I want to protect all routes from my NextJs website to authenticated users with NextAuth except the login route that can be accessed with being authenticated, I tried making a middleware that checks for user token, and redirect them to the login route if it was unavailable. but a general middleware for that would stop the website from getting public files so i came up with this:
// middleware.js
import { NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt'
export default async function middlware(req) {
// ignoring file system and public folder
if (req.nextUrl.pathname.startsWith('/_next/') ||
req.nextUrl.pathname.startsWith('/icons/') ||
req.nextUrl.pathname.startsWith('/fonts/')) {
return;
}
// igonring api/auth
if (req.nextUrl.pathname.startsWith('/api/auth')) {
return;
}
// user session
const session = await getToken({ req })
// redirecting to homepage if a logged in user tries to access the login page
if (req.nextUrl.pathname.startsWith('/auth/login')) {
if (session) {
const url = req.nextUrl.clone()
url.pathname = '/'
return NextResponse.redirect(url)
}
}
// // redirecting to login page if not logged in
if (!req.nextUrl.pathname.startsWith('/auth/login')) {
if (!session && session == null) {
const url = req.nextUrl.clone()
url.pathname = '/auth/login'
return NextResponse.redirect(url)
}
}
}
this works for general routes, but when I try to fetch some data in other pages from other API routes I get this error:
FetchError: invalid json response body at http://localhost:3000/auth/login reason: Unexpected token < in JSON at position 0
at E:\0 - WEB\xwendkaran-admin\node_modules\next\dist\compiled\node-fetch\index.js:1:51227
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async getServerSideProps (webpack-internal:///./src/pages/departments-marking.js:70:22)
at async Object.renderToHTML (E:\0 - WEB\xwendkaran-admin\node_modules\next\dist\server\render.js:490:20)
at async doRender (E:\0 - WEB\xwendkaran-admin\node_modules\next\dist\server\base-server.js:908:38)
at async cacheEntry.responseCache.get.isManualRevalidate.isManualRevalidate (E:\0 - WEB\xwendkaran-admin\node_modules\next\dist\server\base-server.js:1013:28)
at async E:\0 - WEB\xwendkaran-admin\node_modules\next\dist\server\response-cache.js:69:36 {
type: 'invalid-json'
}
this error only occurs because of the middleware and if i remove the middle everything works but that means anyone can access the website and also post data to the server
The problem occurred because when I was trying to fetch data from an API route there was no cookie with the request headers, and !session && session == null would stop it because it showed as not authenticated, so I fixed it with adding user cookies with request headers while fetching the data, like this:
export async function getServerSideProps({ req }) {
try {
// fetching centers
const data = await fetch(`${server}/api/API-ROUTE`, {
method: 'GET',
headers: {
'cookie': req.headers.cookie, // this is where I parsed the cookies
'Content-Type': 'application/json',
'Accept': 'application/json'
},
}).then(res => res.json())
return {
props: {
data
}
}
}
catch (error) {
console.log(error.message)
return {
props: {}
}
}
}

razor pages with firebase auth - where to put this token ? :)

i am working on web site with razor pages. part of the site should be accessed only by registred users. decided to go with firebase authentification (now with login and password ).
created everything necessary in firebase.
created backend code for user registration - works well.
created area which requires authorisation
services.AddRazorPages(options =>
{
options.Conventions.AuthorizeAreaFolder("User", "/");
})
added jwt middleware
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
added code to login page to call firebase to get token
function login()
{
firebase.auth().signInWithEmailAndPassword(email, password)
.then((userCredential) => {
// Signed in
var user = userCredential.user;
// ...
alert("signed");
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
alert(errorMessage);
});
}
got token from firebase.
if i'd call service next, i'd simply put token in "bearer" header.
tried to find how to add header to current browser for future requests and failed.
as i understand, i need this token to be added to auth header ? how ? :)
feeling dumb ;( tried to google, but most samples are for using this token later with api calls.
or i am going in the wrong direction?
tia
ish
well. it seems that it is not possible to add bearer from js, so i switched to cookies
in startup.cs use cookies
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies["bearer"];
return Task.CompletedTask;
}
};
code to login with firebase, put token into the cookie and redirect
function login() {
firebase.auth().signInWithEmailAndPassword(email, password)
.then((userCredential) => {
// Signed in
var user = userCredential.user;
firebase.auth().currentUser.getIdToken(true).then(function (idToken)
{
document.cookie = "bearer" + "=" + idToken;
window.location.href = "/user/index";
}).catch(function (error) {
// Handle error
});
alert("signed");
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
alert(errorMessage);
});
}
or the same with firebaseUI
function login1()
{
ui.start('#firebaseui-auth-container', {
signInSuccessUrl: '/User/index',
signInOptions: [
{
provider: firebase.auth.EmailAuthProvider.PROVIDER_ID,
requireDisplayName: false
}
],
callbacks:
{
signInSuccessWithAuthResult: function (authResult, redirectUrl)
{
var user = authResult.user;
firebase.auth().currentUser.getIdToken(true).then(function (idToken) {
document.cookie = "bearer" + "=" + idToken;
}).catch(function (error) {
// Handle error
});
return true;
}
}
});
}

Lose user data in Nuxt-auth fetchUser CustomStrategy

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

Migrate ADAL.js to MSAL.js

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

How to test single page application with Cypress and Auth0

I am having a single page application hidden behind Auth0 lock, using #auth0/auth0-spa-js. I would like to test it using Cypress, so I have decided to follow the official Auth0 blog post, as well as Johnny Reilly blog post.
I am able to successfully retrieve valid JWT token from auth0 using suggested request. I have no idea what to do with it :(
The trouble I am facing is that both of the above approaches are relying on the app to store the JWT token locally (either in cookie or localstorage). The #auth0/auth0-spa-js is, however, using a different approach, and I assume all the relevant cookies/localstorage is stored on auth0 domains.
Do you have any idea, if there is a way to get around it?
There is a similar issue reported here raised in July 2018, not really providing any solution
I found a resolved issue on #auth0/auth0-spa-js github. The approach suggested by cwmrowe seems to be working
The solution is to mock the response of oauth/token endpoint with token generated on e2e test side.
The approach seems to be working for us
I am copying over the sample code cwmrowe has provided
Cypress.Commands.add(
'login',
(username, password, appState = { target: '/' }) => {
cy.log(`Logging in as ${username}`);
const options = {
method: 'POST',
url: Cypress.env('Auth0TokenUrl'),
body: {
grant_type: 'password',
username,
password,
audience: Cypress.env('Auth0Audience'),
scope: 'openid profile email',
client_id: Cypress.env('Auth0ClientId'),
client_secret: Cypress.env('Auth0ClientSecret')
}
};
cy.request(options).then(({ body }) => {
const { access_token, expires_in, id_token } = body;
cy.server();
// intercept Auth0 request for token and return what we have
cy.route({
url: 'oauth/token',
method: 'POST',
response: {
access_token,
expires_in,
id_token,
token_type: 'Bearer'
}
});
// Auth0 SPA SDK will check for value in cookie to get appState
// and validate nonce (which has been removed for simplicity)
const stateId = 'test';
const encodedAppState = encodeURI(JSON.stringify(appState));
cy.setCookie(
`a0.spajs.txs.${stateId}`,
`{%22appState%22:${encodedAppState}%2C%22scope%22:%22openid%20profile%20email%22%2C%22audience%22:%22default%22}`
);
const callbackUrl = `/auth/callback?code=test-code&state=${stateId}`;
return cy.visit(callbackUrl);
});
}
);
declare namespace Cypress {
interface Chainable<Subject> {
login(
username: string,
password: string,
appState?: any
): Chainable<Subject>;
}
}
Whilst it's not recommended to use the UI to login I do this myself once prior to all tests and then use the silent auth for the tests:- cy.visit("/") silent auths and allows access to the app.
integration/app.js
describe("App", () => {
before(() => {
Cypress.config("baseUrl", "http://localhost:3000");
cy.login();
});
/** Uses silent auth for successive tests */
beforeEach(() => {
cy.restoreLocalStorage();
});
afterEach(() => {
cy.saveLocalStorage();
});
/** tests */
support/commands.js
/**
* Auth0 login
* https://github.com/cypress-io/cypress/issues/461#issuecomment-392070888
*
* Allows silent auth login between tests
*/
let LOCAL_STORAGE_MEMORY = {};
Cypress.Commands.add("saveLocalStorage", () => {
Object.keys(localStorage).forEach(key => {
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
});
});
Cypress.Commands.add("restoreLocalStorage", () => {
Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
});
});
Cypress.Commands.add("clearLocalStorage", () => {
LOCAL_STORAGE_MEMORY = {};
});
For those who has issue with Google Sign in for Cypress look at the plugin: https://github.com/lirantal/cypress-social-logins/
it('Login through Google', () => {
const username = Cypress.env('googleSocialLoginUsername')
const password = Cypress.env('googleSocialLoginPassword')
const loginUrl = Cypress.env('loginUrl')
const cookieName = Cypress.env('cookieName')
const socialLoginOptions = {
username,
password,
loginUrl,
headless: false,
isPopup: true,
logs: false,
loginSelector: 'a[href="/auth/auth0/google-oauth2"]',
postLoginSelector: '.account-panel'
}
return cy.task('GoogleSocialLogin', socialLoginOptions).then(({cookies}) => {
cy.clearCookies()
const cookie = cookies.filter(cookie => cookie.name === cookieName).pop()
if (cookie) {
cy.setCookie(cookie.name, cookie.value, {
domain: cookie.domain,
expiry: cookie.expires,
httpOnly: cookie.httpOnly,
path: cookie.path,
secure: cookie.secure
})
Cypress.Cookies.defaults({
whitelist: cookieName
})
}
})
});