How do i solve JsonWebTokenError: invalid signature RS256 - express

I have been stuck at JsonWebTokenError: invalid signature while trying to verify it.
The auth middleware where i am verifying
module.exports.authMiddleware = (req, res, next) => {
const tokenParts = req.headers.authorization.split(" ");
console.log(tokenParts)
// verifying that the token from authorization header is in correct format
if(tokenParts[0] === "Bearer" && tokenParts[1].match(/\S+\.\S+\.\S+/) !== null){
try {
const verification = jsonwebtoken.verify(
tokenParts[1],
PUBLIC_KEY,
{algorithms: ["RS256"]}
)
req.jwt = verification
next()
} catch (error) {
console.log(error)
res.status(401).json({
success: false,
message: "You are not authorized auth"
})
}
} else {
res.status(401).json({
success: false,
message: "You are not authorized",
})
}
}
I have successfuly generated the private and public keys here is the function
const genKeyPair = () => {
const keyPair = crypto.generateKeyPairSync('rsa', {
modulusLength: 4096, // bits - standard for RSA keys
publicKeyEncoding: {
type: 'pkcs1', // "Public Key Cryptography Standards 1"
format: 'pem' // Most common formatting choice
},
privateKeyEncoding: {
type: 'pkcs1', // "Public Key Cryptography Standards 1"
format: 'pem' // Most common formatting choice
}
});
// Create the public key file
fs.writeFileSync("keys/id_rsa_pub.pem", keyPair.publicKey);
// Create the private key file
fs.writeFileSync("keys/id_rsa_priv.pem", keyPair.privateKey);
}
genKeyPair()
When i copy the token that is generated during login or registration and add the private key and public key to jwt.io online with algorithm, i get signature verified
What could possible be the problem?

Related

Next-auth how to decrypt session-token

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()
}

Multiple Issuers for common-endpoint

According to the passport-azure-ad documentation, I should be able to specify a list of issuers as a string array.
However, I'm having trouble getting the second example below to work.
The first example works fine:
Example 1 - Works with a token issued by <TENANT_1_GUID>
{
identityMetadata: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
issuer: [
'https://login.microsoftonline.com/<TENANT_1_GUID>/v2.0'
],
clientID: '<APP_GUID>',
validateIssuer: true,
}
Example 2 - Does not work with a token issued by <TENANT_1_GUID> or <TENANT_2_GUID>
But my problem is that that the token is not successfully validated when a list of acceptable issuers is provided:-
{
identityMetadata: 'https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration',
issuer: [
'https://login.microsoftonline.com/<TENANT_1_GUID>/v2.0',
'https://login.microsoftonline.com/<TENTANT_2_GUID>/v2.0'
],
clientID: '<APP_GUID>',
validateIssuer: true,
}
Is this not the correct way to validate a token that could have come between one of two tenants?
Thanks!
I've come up with something that works... created a middlewear with the passport in it:-
const jwt = require('jsonwebtoken');
const passport = require('passport');
const BearerStrategy = require('passport-azure-ad').BearerStrategy;
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(" ")[1];
decodedToken = jwt.decode(token);
let issuer;
const iss_company1 = "https://login.microsoftonline.com/<TENANT_1_GUID>/v2.0";
const iss_company2 = "https://login.microsoftonline.com/<TENANT_2_GUID>/v2.0";
switch (decodedToken.iss) {
case iss_company1:
issuer = iss_company1;
break;
case iss_company2:
issuer = iss_company2;
break;
default:
issuer = iss_company1;
}
let passportOptions = {
identityMetadata: process.env.passport_identityMetadata,
issuer,
clientID: process.env.passport_clientID,
passReqToCallback: process.env.passport_passReqToCallback,
loggingLevel: "error",
loggingNoPII: true
};
let bearerStrategy = new BearerStrategy(passportOptions,
function(token, done) {
return done(null, token);
}
);
passport.use(bearerStrategy);
passport.authenticate('oauth-bearer', { session: false })(req, res, next);
} catch (error) {
res.status(401).json({ message: "Unauthorized" });
}
};

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

Auth0 re-login after token expire does not display login window

I'm working with Auth0, I have a problem where after user token expire and user try to relogin, it doesn't redirect user to login window at all instead it just automatically logged in when user click on login link.
They are fine if I manually log out then re-login, then it will ask for authentication again.
I tried removing all the localstorage memory regarding the user but it still doesn't fix it.
export const expiredAtKey = 'expired_at';
export const uidKey = 'uid';
export const urlStateKey = 'urlState';
#Injectable()
export class Auth {
auth0 = new auth0.WebAuth({
clientID: environment.auth0ClientId,
domain: environment.auth0Domain,
responseType: 'token id_token',
redirectUri: `${constants.ORIGIN_URL}/auth`,
scope: 'openid email'
});
constructor(private router: Router,
public dialog: MatDialog,
private http: HttpClient) {
}
public handleAuthentication(): void {
this.auth0.parseHash(this.handleAuthResult);
}
public login() {
//I have tried to clear local storage everytime user call login to prevent this to happen, but it still skip the login window
this.clearLocalStorage();
localStorage.setItem(urlStateKey, location.pathname);
this.auth0.authorize();
};
public signUp(email, password, cb) {
this.auth0.signupAndAuthorize({
email: email,
password: password,
connection: environment.auth0Connection
}, cb);
}
public authenticated() {
const exp = localStorage.getItem(expiredAtKey);
if (!exp) {
return false;
}
const expiresAt = JSON.parse(localStorage.getItem(expiredAtKey));
return new Date().getTime() < expiresAt;
};
public logout() {
this.clearLocalStorage();
window.location.href = `https://${ environment.auth0Domain }/v2/logout?returnTo=${ constants.ORIGIN_URL }`;
};
public setSession(authResult): void {
const idToken = jwtDecode(authResult.idToken);
localStorage.setItem('idToken', authResult.idToken);
localStorage.setItem(uidKey, idToken.email);
localStorage.setItem('userId', idToken.sub);
const expiresAt = JSON.stringify(idToken.exp * 1000);
localStorage.setItem(expiredAtKey, expiresAt);
}
private handleAuthResult = (err, authResult) => {
if (err) {
if (!environment.production) {
console.log(err);
}
if(err.errorDescription === "Please verify your email before logging in."){
this.dialog.open(
ErrorDialogComponent,
{ data: "Please verify your email before logging in."}
);
this.router.navigate(['/initiatives'])
}else{
this.dialog.open(
ErrorDialogComponent,
{ data: "An error occurred while trying to authenticate. Please ensure private browsing is disabled and try again."}
);
this.router.navigate(['/initiatives'])
}
} else if (authResult && authResult.idToken && authResult.idToken !== 'undefined') {
this.setSession(authResult);
const path = localStorage.getItem(urlStateKey);
this.router.navigateByUrl(path);
}
};
clearLocalStorage() {
localStorage.removeItem(expiredAtKey);
localStorage.removeItem(uidKey);
localStorage.removeItem(urlStateKey);
localStorage.removeItem('userId')
}
}
I want user to do the authentication again after the token is expired.
This is happening due to SSO cookie set in the server to maintain the session. To clear the server-side session, you need to redirect the user to /logout endpoint when token expires. The logout method does that.
https://auth0.com/docs/sso/current/single-page-apps

mocha : testing a signup process with email-verification framework

I try to test my signup router post function which use email-verification framework,but mocha shows me the error message below :
TypeError: Cannot read property 'findOne' of null
This my code :
function that invoke the error:
nev.resendVerificationEmail(email,(err,userExist) =>{
if(err){
return res.status(404).send('Error : resending verification email failed');}
if(userExist){
res.json({message : 'An email has been sent to you , again. Please check it to verify your account'});
}else{
res.json({message : 'Your verification code has expired . Please sign up again'});
and this is the implementation of resendVerificationEmail function
var resendVerificationEmail = function(email, callback) {
var query = {};
query[options.emailFieldName] = email;
options.tempUserModel.findOne(query, function(err, tempUser) { //this the error handler I guess
if (err) {
return callback(err, null);
}
// user found (i.e. user re-requested verification email before expiration)
if (tempUser) {
// generate new user token
tempUser[options.URLFieldName] = randtoken.generate(options.URLLength);
tempUser.save(function(err) {
if (err) {
return callback(err, null);
}
sendVerificationEmail(getNestedValue(tempUser, options.emailFieldName), tempUser[options.URLFieldName], function(err) {
if (err) {
return callback(err, null);
}
return callback(null, true);
});
});
} else {
return callback(null, false);
}
});
};
and this is my spec
describe(' SignUp : /POST Test with fake client request : ', () => {
let req, res, statusCode, sendData,user;
beforeEach((done) => {
SignupModal.remove({}, (err) => {
done();
});
user = {
firstName : 'ben',
secondName : 'wissem',
username : 'wiss',
email : 'xy#zt.sq',
password : 'wissem'
};
res = {
json: function (code, data) {
statusCode = code;
sendData = data;
}
};
});
it('should send 200 code', () => {
chai.request(server)
.post('/user/signup')
.send(user)
.end((err, res) => {
res.statusCode.should.equal(200);
});
});
Can any one help me please ?!
if you want to end-to-end test email verification try EmailE2E.com, it's a free API that let's you send and receive emails from randomly generated inboxes. It's perfect for Firebase, Amazon Cognito, or other OAuth providers that use email verification codes during sign up. It also has a Javascript client you can use with Mocha.