Jwt cannot be parsed, Okta - javascript

I am trying to set up Okta authentication on my React App.
On the client side I am able to authenticate successfully and I get the access token. However when I try to authenticate a backend service using OktaJwtVerfier, I get the error message:
'Jwt cannot be parsed. SyntaxError: Unexpected token in JSON at position 0'
I have develop a very simple test program to test the verification of the token, so basically I get authenticated on the browser, I copy paste the jwt token in my small script to test authentication, and it fails with the message above, what am I doing wrong?
const OktaJwtVerifier = require('#okta/jwt-verifier');
const oktaJwtVerifier = new OktaJwtVerifier({
issuer: "https://dev-XXXXX.oktapreview.com/oauth2/default",
clientId: "XXXXXX",
assertClaims: {
'aud': 'api://default',
'cid': "XXXXXX",
},
});
const accessToken = 'Bearer eyJraWQiO.....';
oktaJwtVerifier.verifyAccessToken(accessToken).then((jwt) => {
console.log('auth succesfulll', jwt);
}).catch((e)=> {
console.log(e);
})

The comment by #jps is correct. Your header has a value of Bearer XXXX, where XXXX is the actual JWT string to parse.
Here's an example from the Okta project of how they do it in an Express app:
const authHeader = req.headers.authorization || '';
const match = authHeader.match(/Bearer (.+)/);
if (!match) {
res.status(401);
return next('Unauthorized');
}
const accessToken = match[1];
You can see the code in its full context here.
your code could be modified as follows:
const headerValue = 'Bearer eyJraWQiO.....';
const match = headerValue.match(/Bearer (.+)/);
if (!match) {
throw new Error('your error message here')
}
const accessToken = match[1];
oktaJwtVerifier.verifyAccessToken(accessToken).then((jwt) => {
console.log('auth succesfulll', jwt);
}).catch((e)=> {
console.log(e);
})

Related

request to login to a node server, using react

Here is the situation:
I have a database which contains a user and password registered.
My assignment, for now, is to create a login form, and login with a registered uname and pw.
Uname and pw are registered in the server/database already.
ps: I did not create the server nor database.
Node server code
import express from 'express';
import cors from 'cors';
import http from 'http';
import { Sequelize } from 'sequelize';
import { Data } from './database';
import { router } from './routes/Router';
import { initialData } from './database/someData';
const closeServer = async (
server: http.Server,
sequelize: Sequelize,
signal?: string
) => {
server.close();
await sequelize.close();
process.exit();
};
const runServer = async (): Promise<void> => {
const PORT = process.env.PORT || 8082;
const app = express();
const sequelize = Data.init();
app.use(
cors({
credentials: true,
origin: 'http://localhost:3000',
})
);
app.use('/api', router);
const server = app.listen(PORT, () => {
console.log(`Starting server at ${PORT}`);
});
try {
await sequelize.authenticate();
await sequelize.sync({
force: process.env.SERVER === 'reset',
});
if (process.env.SERVER === 'reset') await initialData();
} catch (e) {
closeServer(server, sequelize);
throw e;
}
};
runServer()
.then(() => {
console.log('Run successfully');
})
.catch((ex: Error) => {
console.log('Unable to run:', ex);
});
I need help on what is that I have to do.
When I input username and pw, on the form, what are the methods to use for sending the info?
And then, when the info reaches the server, i think the username and pw need to be validated with jwt, and then check if the user and pw exists. how do i do that?
What i have understood so far is that i gotta use axios to send info to server, but thats it.
Do i need to use jwt for the login?
What is the normal flow for this kind of mechanism?
I am using react as a framework.
So there are quite few steps here.
First you have to create endpoint on your backend server for issuing jwt tokens. Jwt tokens can be used as a pass for user to login. So in your router you would add something like this:
router.post('/login', (req, res)=> {
const username = req.body.username
const password = req.body.password
// Then you make db call to verify username and password are correct.
if Credentials are valid, you would issue jwt token
jwt.sign({
// here you can save extra information of user. Also remember this information must be public since anyone can see it. Do not put user password here
email: 'email',
userId: 'id',
}, "secret")
})
After this, you need some kind of middleware on backend, so that on each user request, you check and verify this jwt token which is sent from react application. For example you could write isAuth middleware:
const jwt =require("jsonwebtoken");
export const isAuth= (req, res, next) => {
try {
// here we attach request in auth header, with Bearer "jwt token" format. So we extract jwt token and verify it
const authHeader = req.get("Authorization");
if (!authHeader) {
return res.status(401).json({ message: "no token" });
}
const token = authHeader.split(" ")[1];
let decodedToken;
decodedToken = jwt.verify(token, "secret");
if (!decodedToken) {
return res.status(401).json({ message: "Wrong token" });
}
req.userId = decodedToken.userId;
next();
} catch (err) {
console.error(err);
return res.status(401).json({ message: err });
}
};
Now you would be able to have backend endpoints like this:
// This is how you would require login on some routes
router.post("/getMyPrivateInfo", isAuth, QueryPrivatInfo)
Now on React side, you would make request for login like this:
axios.post("/login", {
username: '1',
password: "2"
})
This would return jwt token, now you would save this token in local storage.
After its saved in local storage and you make request with axios for private info you would do following
axios.post("/getMyPrivateInfo", {any request body info neeeded}, {
headers: {
Authorization: "Bearer jwtTokenFromLocalStorage"
}
})
This is how whole flow will work, hope it makes sense

Redirect R Shiny to local host after oauth token authentication for Azure AD

I am creating a Shiny app that involves AAD token authentication to connect to Snowflake.
I am using a .js script below that obtains the token:
async function wrapperFunc() {
const msalConfig = {
auth: {
clientId: 'XXXXXXXXX',
authority: 'https://login.microsoftonline.com/XXXXXXXXXXX'
}
};
const msalInstance = new msal.PublicClientApplication(msalConfig);
const silentRequest = {
scopes: ["api://XXXXXX/session:role-any"]
};
const callLogin = async function(silentRequest, msalInstance) {
try {
const loginResponse = await msalInstance.loginPopup(silentRequest);
return loginResponse;
} catch (err) {
console.log(err)
}
}
response = callLogin(silentRequest, msalInstance);
return response;
}
wrapperFunc().then(result => {
Shiny.setInputValue("oauthToken", result['accessToken']);
console.log(result['accessToken']);
});
and then plugging that token into the following db connection:
pii_db_connection <- function(OAuth_token) {
connection <- DBI::dbConnect(
drv = odbc::odbc(),
dsn = "snowflake",
token = OAuth_token,
authenticator = "oauth"
)
return(connection)
}
I am redirected to the browser window to log in, and then once that's done, I get hit with this error message:
AADSTS50011: The redirect URI 'XXXXX' specified in the request does not match the redirect URIs configured for the application 'XXXXX'. Make sure the redirect URI sent in the request matches one added to your application in the Azure portal.
I can access the app by changing the URL to a localhost URL. But my question is, can I get it to automatically redirect the browser window to the localhost URL?

Authorization header value returns undefined in SuperTest

So I am writing tests using Mocha, Supertest and Chai. All my API's require an Authorization header which can be gotten from req.headers["authorization"]. This is the current setup of what my test looks like.
...
describe("Upload media files", () => {
it("uploads a file successfully", async () => {
const imagePath = path.join(__dirname, "../../fixtures/profile.jpeg");
const res = await app.post("/api/v1/account/upload") //app is global
.set("Authorization", "ey83hfjksksls...") //right here
.set("content-type", "multipart/form-data")
.attach("media", imagePath);
expect(res.status).to.eql(200);
expect(res.body.data).not.to.be.null;
expect(res.body.data).to.match(/https:/);
});
});
My tests always fail because a 401 is always returned for some reason that I do not know. Here's the function that handle's the decoding of the JWT
async function decodeToken() {
const sessionUser = req.session.userId;
const token = req.headers["authorization"] // token value is present here.
if (!token && !sessionUser) {
return res.status(401).json({
error: "Access denied, no valid credentials.",
});
}
if (token) {
const secret = sails.config.custom.jwtSecret || process.env.JWT_SECRET;
const decoded = jwt.verify(token, secret);
req.me = decoded;
return proceed();
}
const user = await User.findOne({
id: sessionUser,
});
req.me = user;
return proceed();
}
From the function above, when I do a console.log of the token variable, I do get a long string returned which shows that the token is being generated and passed into the .set('Authorization', 'eyeur9339..') header, but the test still fails. I don't know what to do anymore. Any help is appreciated, thank you.
if you use jwt for authorization, use Bearer before token so you should write Bearer, like this :
Bearer 5d5bd7f7e35a86541686f96c9cdd72ed67f5ba223811634e0319af224164823edc2d0090d1f6c2c3b1dec842ea783c71feb443ac2d26e
and for get token from header do like this :
const token = req.headers.authorization.split(' ')[1]
const decodedToken = jwt.verify(token, secret)

Trying to decode jwt token from server side

I have created a CLI with which I consume a data from a rest api I build with node.js and express.js. In the cli the user has to login and after that when he performs a query his data are passed as a header using jwt. This I have done with the following code:
The Login.ts file has this part of the code for the header creation:
await connection.query(`SELECT user,pass,email,quota,apikey,privileges FROM users WHERE user=?`,[`${flags.user}`], function (err, result, fields) {
if (err) throw err;
//console.log(`${flags.user}`);
//console.log(result[0].pass);
var u=result[0].user;
var p=result[0].pass;
var e=result[0].email;
var q=result[0].quota;
var a=result[0].apikey;
var p=result[0].privileges;
const password=result[0].pass;
if(bcrypt.compareSync(`${flags.passw}`, password)) {
var fs=require('fs');
var privateKey = fs.readFileSync('private.key');
var jwt=require('jsonwebtoken');
var token = jwt.sign({user:`${flags.user}`,passw:`${flags.passw}`,email: e, quota: q,apikey: a,privileges: p }, privateKey, { algorithm: 'RS256' });
fs.writeFileSync("temptoken.txt",token);
} else {
console.log("Wrong username or password\n");
}
});
After the login the user can perform various queries, where in their code they perform an
axios.get where I pass the header like this:
const axios = require('axios');
const {flags} = this.parse(ActualTotalLoadCommand);
var fs=require('fs');
var token = fs.readFileSync('temptoken.txt');
axios.defaults.headers.common['Authorization']=token;
From my server side file where I have the file entry.routes.js how can I capture the token and then decode it to test for example if the user who performed the query is a valid user?
The entry.routes.js looks like this:
module.exports = app => {
const entry = require("../controlers/entry.controller.js");
// Retrieve a single Entry with Id
var fs=require('fs');
var privateKey = fs.readFileSync('/home/vangelis/softeng/energy/private.key');
var jwt=require('express-jwt');
app.use(
jwt({
secret: privateKey,
credentialsRequired: false,
getToken: function fromHeaderOrQuerystring (req) {
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') {
return req.headers.authorization.split(' ')[1];
} else if (req.query && req.query.token) {
return req.query.token;
}
return null;
}
}));
app.get("/entry/:Id", entry.findOne);
app.get("/energy/api/ActualTotalLoad/:AreaName/:Resolution/date/:Year-:Month-:Day", entry.findTwo);
As you can see I tried using express-jwt, but I can't make it work.
In your code above you create the header with:
axios.defaults.headers.common['Authorization']=token;
which will result in
Authorization: <the token>
but in the code to read the header :
if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer')
you expect a header like this:
Authorization: Bearer <the token>
which is the standard way. So you should add Bearer to the line to create the header.
Beside that, I see you're putting the (encrypted) password into the token. That's something you should avoid. There'sno good reason to do it. You have authenticated the user before you created the token and now, when you receive the token, you verify the authenticity of that token by it's signature.

Firebase auth user.getIdToken() sending expired tokens

As far as I know, getIdToken() should by default send an unexpired token to the server, handling the token refreshing internally. However, I am getting many many errors from my server regarding expired tokens.
On my frontend, I created axios instance as follows:
const base = axios.create({
baseURL: apiUrl,
});
base.interceptors.request.use(async (config) => {
const token = await getAccessToken();
config.headers.Authorization = `Bearer ${token}`;
return config;
});
const getAccessToken = async () => {
const user = firebase.auth().currentUser;
if (user) {
const token = await user.getIdToken();
return token;
}
return null;
};
Then on the receiving server, I verify the token by doing:
const admin = require("firebase-admin");
const auths = req.headers.authorization.split(" ");
admin.auth().verifyIdToken(auths[1]).then(async token => {
...
});
The error I receive from the server is:
Error: Firebase ID token has expired. Get a fresh ID token from your client app and try again (auth/id-token-expired). See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
If I change getIdToken() to getIdToken(true), the issue goes away.
I verified that the getAccessToken method is called for every request made. I'm a bit lost why my server continues to complain about expired tokens, any help would be appreciated.

Categories

Resources