Auth0 & Next-Auth Malformed JWT - node.js

I am attempting to setup authentication in my NextJS project and I am using Next-Auth. I am currently trying to setup a simple GET /me route that would be hit through React Query using the access_token retrieved by a successful Auth0 session Login.
BUT: the access_token received form Next-Auth w/ Auth0 useSession() is malformed
EDIT: I think the issue is that next-auth / auth0 is storing the token as an encrypted JWE. I need to figure out how to decrypt this and pass it to my api
https://github.com/nextauthjs/next-auth/issues/243
https://github.com/nextauthjs/next-auth/pull/249
https://github.com/nextauthjs/next-auth/discussions/5214
FRONTEND
in my pages > api > auth > [...nextauth].js I have the following configuration
const authOptions = {
providers: [
Auth0Provider({
clientId: process.env.AUTH0_CLIENT_ID,
clientSecret: process.env.AUTH0_CLIENT_SECRET,
issuer: process.env.AUTH0_ISSUER,
idToken: true,
}),
],
// Configure callbacks 👉 https://next-auth.js.org/configuration/callbacks
callbacks: {
// The JWT callback is called any time a token is written to
jwt: ({ token, user, account, profile, isNewUser }) => {
if (account) {
token.access_token = account.access_token;
token.id_token = account.id_token;
token.auth0_id = token.sub;
token.type = account.token_type;
}
delete token.name;
delete token.picture;
delete token.sub;
return token;
},
// The session callback is called before a session object is returned to the client
session: ({ session, user, token }) => {
const newSession = {
user: {
auth0_id: token.auth0_id,
email: token.email,
},
token: {
access_token: token.access_token,
id_token: token.id_token,
token_type: token.type,
},
};
return newSession;
},
},
secret: process.env.NEXTAUTH_SECRET,
};
export default NextAuth(authOptions);
Auth0 Config
in Auth0 Dashboard: Auth0 > Applications > Applications > <PROJECT_NAME> > AdvancedSettings > OAuth the signature algorithm is RS256
Successful Login Landing Page
here I am using const { data: session, status } = useSession(); to extract the value of the current session (which matches the shape created in the session callback of pages > api > auth > [...nextauth].js -- and has the access_token)
_app.jsx component
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
return (
<SessionProvider session={session}>
<QueryClientProvider client={queryClient}>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</SessionProvider>
);
}
API Requests (from the frontend)
for each API request to the server I am setting the headers as such
if (token) headers["authorization"] = `Bearer ${token}`;
headers["Content-Type"] = "application/json";
Server Middleware
I created an auth middleware function that serves 2 purposes.
Validate the JWT passed to the route (🚨 THIS IS WHERE THINGS BREAK 🚨)
Attempt to find a user in my postgres DB with matching auth0_id (auth0|)
below is the auth middleware
// Auth0
import { isPublicRoute } from "../services/auth0/index.js";
import { expressjwt } from "express-jwt";
import jwks from "jwks-rsa";
// 👀 I have copied this directly from the Auth0 Dashboard: Applications > APIs > QuickStart
// 🚨 the express-jwt library is failing - error below
const validator = expressjwt({
secret: jwks.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: "https://dev-ikfyop4g.us.auth0.com/.well-known/jwks.json",
}),
audience: "thunderbolt",
issuer: "https://dev-ikfyop4g.us.auth0.com/",
algorithms: ["RS256"],
});
// NOTE: 👀 we are not actually getting to this function
// This function will retrieve the user and feed it into the request
// into a populated user model, if the id is not in the database it
// will create a new user and pull the base data from auth0
const userInjector = async (req, res, next) => {
if (isPublicRoute(req)) return next();
if (!req.auth0?.sub) throw badImplementation("JWT missing in userInjector");
req.user = await userFromReq(req);
console.log("THE USER INJECTOR RESULT :: req.user", req.user);
next();
};
const auth = () => {
return [validator, userInjector];
};
export default auth;
Inside my server I am importing this auth middleware function and using like this
server.use(auth());
THE express-jwt ERROR
UnauthorizedError: jwt malformed
at new UnauthorizedError (/Users/mrt/Documents/MrT/code/M/bolt/node_modules/express-jwt/dist/errors/UnauthorizedError.js:22:28)
at /Users/mrt/Documents/MrT/code/M/bolt/node_modules/express-jwt/dist/index.js:133:35
at step (/Users/mrt/Documents/MrT/code/M/bolt/node_modules/express-jwt/dist/index.js:33:23)
at Object.next (/Users/mrt/Documents/MrT/code/M/bolt/node_modules/express-jwt/dist/index.js:14:53)
at fulfilled (/Users/mrt/Documents/MrT/code/M/bolt/node_modules/express-jwt/dist/index.js:5:58)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
The access_token that I am receiving from Auth0 is malformed. Upon further investigation I would agree. The AccessToken I am receiving (as a JWT) has 5 parts (parts are strings separated by a period ..
Access Token From Auth0 Session
eyJhbGc<REMOVED_CHUNK>uYXV0aDAuY29tLyJ9.. // 2 period w/ nothing inbetween??
zOKQ2<REMOVED_CHUNK>rSm6.
9qbS5yndGKkrZ9Tc_dL8ZOuHtp_-e58uGqvGHgcpcFS8-s6SEHJYZ0_g7Ii7aYQe4AdbeK9ekW-704X_6C1r5JH3-9yBz<REMOVED_CHUNK>6Rn3Q8U0YC_x8Vp9pF_EA4GHjevXrh3HFBzCY4AEAx-Rmnzk4tZDgk3oU2rsY1NleMTwpIj0h29KIsukg113uMt5KCWKVnosSI-psaBu<REMOVED_CHUNK>lf0R_y5ClcXF6XY0ezIvuwoSQOmhulMlPsTxzBVGeoIhsooNntgAc4s.
ojmkoO_CO<REMOVED_CHUNK>URg
If everything is configured for RS256 why is Next-Auth w/ Auth0 sending me a malformed JWT.
EDIT: a 5 part JWT is a JSON Web Encryption (JWE) token...?

Related

Passing Keycloak bearer token to express backend?

We have a frontend application that uses Vue3 and a backend that uses nodejs+express.
We are trying to make it so once the frontend application is authorised by keycloak it can then pass a bearer token to the backend (which is also protected by keycloak in the same realm), to make the API calls.
Can anyone suggest how we should be doing this?
Follows is what we are trying and seeing as a result.
The error thrown back is simply 'Access Denied', with no other details Running the debugger we see a 'invalid token (wrong audience)' error thrown in the GrantManager.validateToken function (which unfortunately doesn't bubble up).
The frontend makes use of #dsb-norge/vue-keycloak-js which leverages keycloak-js.
The backend makes use of keycloak-connect. Its endpoints are REST based.
In the webapp startup we initialise axios as follows, which passes the bearer token to the backend server
const axiosConfig: AxiosRequestConfig = {
baseURL: 'http://someurl'
};
api = axios.create(axiosConfig);
// include keycloak token when communicating with API server
api.interceptors.request.use(
(config) => {
if (app.config.globalProperties.$keycloak) {
const keycloak = app.config.globalProperties.$keycloak;
const token = keycloak.token as string;
const auth = 'Authorization';
if (token && config.headers) {
config.headers[auth] = `Bearer ${token}`;
}
}
return config;
}
);
app.config.globalProperties.$api = api;
On the backend, during the middleware initialisation:
const keycloak = new Keycloak({});
app.keycloak = keycloak;
app.use(keycloak.middleware({
logout: '/logout',
admin: '/'
}));
Then when protecting the endpoints:
const keycloakJson = keystore.get('keycloak');
const keycloak = new KeycloakConnect ({
cookies: false
}, keycloakJson);
router.use('/api', keycloak.protect('realm:staff'), apiRoutes);
We have two client configured in Keycloak:
app-frontend, set to use access type 'public'
app-server, set to use access type 'bearer token'
Trying with $keycloak.token gives us the 'invalid token (wrong audience)' error, but if we try with $keycloak.idToken instead, then we get 'invalid token (wrong type)'
In the first case it is comparing token.content.aud of value 'account', with a clientId of app-server. In the second case it is comparing token.content.typ, of value 'ID' with an expected type of 'Bearer'.
Upon discussion with a developer on another projects, it turns out my approach is wrong on the server and that keycloak-connect is the wrong tool for the job. The reasoning is that keycloak-connect is wanting to do its own authentication flow, since the front-end token is incompatible.
The suggested approach is to take the bearer token provided in the header and use the jwt-uri for my keycloak realm to verify the token and then use whatever data I need in the token.
Follows is an early implementation (it works, but it needs refinement) of the requireApiAuthentication function I am using to protect our endpoints:
import jwksClient from 'jwks-rsa';
import jwt, { Secret, GetPublicKeyOrSecret } from 'jsonwebtoken';
// promisify jwt.verify, since it doesn't do promises
async function jwtVerify (token: string, secretOrPublicKey: Secret | GetPublicKeyOrSecret): Promise<any> {
return new Promise<any>((resolve, reject) => {
jwt.verify(token, secretOrPublicKey, (err: any, decoded: object | undefined) => {
if (err) {
reject(err);
} else {
resolve(decoded);
}
});
});
}
function requireApiAuthentication (requiredRole: string) {
// TODO build jwksUri based on available keycloak configuration;
const baseUrl = '...';
const realm = '...';
const client = jwksClient({
jwksUri: `${baseUrl}/realms/${realm}/protocol/openid-connect/certs`
});
function getKey (header, callback) {
client.getSigningKey(header.kid, (err: any, key: Record<string, any>) => {
const signingKey = key.publicKey || key.rsaPublicKey;
callback(null, signingKey);
});
}
return async (req: Request, res: Response, next: NextFunction) => {
const authorization = req.headers.authorization;
if (authorization && authorization.toLowerCase().startsWith('bearer ')) {
const token = authorization.split(' ')[1];
const tokenDecoded = await jwtVerify(token, getKey);
if (tokenDecoded.realm_access && tokenDecoded.realm_access.roles) {
const roles = tokenDecoded.realm_access.roles;
if (roles.indexOf(requiredRole) > -1) {
next();
return;
}
}
}
next(new Error('Unauthorized'));
};
}
and then used as follows:
router.use('/api', requireApiAuthentication('staff'), apiRoutes);

DocuSign Get JWT Token MEAN Stack

Building a basic application where users can find Service Providers using MEAN Stack, and after negotiations are over, agreements are auto generated and have to be signed by both parties.
Got Stuck on generation of JWT Token for authentication.
Steps I followed are:
Generate a url for obtaining consent from user and pass it to frontend. Users will be redirected and permissions can be granted from there.
var url = "https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature&client_id=42017946-xxxx-xxxx-xxxx-81b0ca97dc9a&redirect_uri=http://localhost:4200/authorization_code/callback";
res.status(200).json({
status: 1,
message: 'Fetched',
value: url
});
After successful redirection with code in URL, API call is made to backend for the generation of JWT token.
Token is generated as follows:
var jwt = require('jsonwebtoken');
var privateKey = fs.readFileSync(require('path').resolve(__dirname, '../../src/environments/docusign'));
const header = {
"alg": "RS256",
"typ": "JWT"
};
const payload = {
iss: '42017946-xxxx-xxxx-a5cd-xxxxxx',
sub: '123456',
iat: Math.floor(+new Date() / 1000),
aud: "account-d.docusign.com",
scope: "signature"
};
var token = jwt.sign(payload, privateKey, { algorithm: 'RS256', header: header });
Private key used above is from docusign admin panel.
iss -> Integration key against my app.
sub -> user id in the drop down of user symbol in admin panel
Obtain the access token
const axios = require('axios');
axios.post('https://account-d.docusign.com/oauth/token',
{
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: token
})
.then(resposne => {
console.log(response);
})
.catch(err => {
if (err.response) {
console.log(err);
} else if (err.request) {}
else {}
})
But I am constantly getting error: { error: 'invalid_grant', error_description: 'no_valid_keys_or_signatures' }
I would suggest using the node.JS SDK or npm package and using the build-it JWT method to authenticate. The code would look like this:
(click here for GitHub example)
DsJwtAuth.prototype.getToken = async function _getToken() {
// Data used
// dsConfig.dsClientId
// dsConfig.impersonatedUserGuid
// dsConfig.privateKey
// dsConfig.dsOauthServer
const jwtLifeSec = 10 * 60, // requested lifetime for the JWT is 10 min
scopes = "signature", // impersonation scope is implied due to use of JWT grant
dsApi = new docusign.ApiClient();
dsApi.setOAuthBasePath(dsConfig.dsOauthServer.replace('https://', '')); // it should be domain only.
const results = await dsApi.requestJWTUserToken(dsConfig.dsClientId,
dsConfig.impersonatedUserGuid, scopes, rsaKey,
jwtLifeSec);
const expiresAt = moment().add(results.body.expires_in, 's').subtract(tokenReplaceMin, 'm');
this.accessToken = results.body.access_token;
this._tokenExpiration = expiresAt;
return {
accessToken: results.body.access_token,
tokenExpirationTimestamp: expiresAt
};

Battle.net API returns 401 Error using OAuth Token

I'm using Nodejs with express on the Battle.net API to generate an Oauth Token. https://develop.battle.net/documentation/guides/using-oauth
Generating the token itself works, as it returns me the token. But when I use the code to make a request to their API, for example:
https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN
I get a 401 Unauthorized Error Response, debug log:
{ url:
'https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN',
status: 401,
statusText: 'Unauthorized',
headers: Headers { [Symbol(map)]: [Object] } }
I'm trying to fetch the members of a guild via fetch().
I already tried:
Creating a new Application (with new Client Secret and ID)
setting every possible callback url in the battle.net settings:
https://localhost/
http://localhost/
https://localhost:443/
http://localhost:443/
https://localhost/auth/bnet/callback
http://localhost/auth/bnet/callback
https://localhost:443/auth/bnet/callback
http://localhost:443/auth/bnet/callback
creating a token manually by "trying out the api" (https://develop.battle.net/documentation/api-reference/world-of-warcraft-community-api), where you put in your Client ID and Secret and then get a temporary Token. THAT ONE works, also in my application.
You can compare response of those two urls (just use your browser):
First (Generated in my application): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=EU7XD8E4K9IAJKBGJSP3MDBLAVCIU2BYXS
Second (Generated trying out the API on battle.net website where you fill out clientid and secret to test out the api): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=US23su4g0hAeS5w3EUCkKA9MJPgJ8k8bzV
CODE
server.js, simple express app
var BNET_ID = "MYID";
var BNET_SECRET = "MYSECRET";
...
// Use the BnetStrategy within Passport.
passport.use(
new BnetStrategy(
{ clientID: BNET_ID,
clientSecret: BNET_SECRET,
scope: "wow.profile sc2.profile",
callbackURL: "https://localhost/",
region: "eu" },
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
return done(null, profile);
});
})
);
// bnet auth routes
app.get('/auth/bnet', passport.authenticate('bnet'));
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/' }),
function(req, res){
res.redirect('/');
});
controller.js
...
const res = await fetch(`https://eu.api.blizzard.com/wow/guild/${servers[iterator]}/The new Dimension?fields=members&locale=de_DE&access_token=${thetoken}`).then((res) => {
res.json();
// for debugging, shows 401 Error
console.log(res);
});
...
I actually expect a response such as this, because it works using a temporary Token:
status: 200 OK
body: {
"lastModified": 1546676373000,
"name": "The new Dimension",
"realm": "Blackmoore",
"battlegroup": "Glutsturm / Emberstorm",
"level": 25,
"side": 0,
"achievementPoints": 1005,
"members":
(......)
}
I managed to resolve the issue!
Very, very hacky but I managed to resolve the issue by hacking the oauth callback middleware like this:
set my used API token to the req.user.token.
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/?error' }),
function(req, res) {
req.session.bnettoken = req.user.token;
res.redirect('/');
}
);
I suspect that "code" or "token" is also used in my SessionStorage (express-session) to store the current session in my database. So I just hack the user.token out of the request and use that. Phew.. Hours of work.
From the documentation I can see that you need to pass the token into the Authorization header with value of : Bearer HEREISMYTOKEN.
More info on Authorization Header and Headers here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Headers
Example on how to use it can be found in this SO answer

How to provide frontend with JSON web token after server authentication?

So far I have only dealt with server-rendered apps, where after a user logs in via username/password or using an OAuth provider (Facebook etc.), the server just sets a session cookie while redirecting to the relevant page.
However now I'm attempting to build an app using a more 'modern' approach, with React on the frontend and a JSON API backend. Apparently the standard choice for this is to use a JSON web token for authentication, however I'm having trouble working out how I'm meant to provide the JWT to the client so it can be stored in session/local storage or wherever.
Example to illustrate better:
User clicks link (/auth/facebook) to log in via Facebook
User is redirected and shown Facebook login form and/or permission dialog (if necessary)
Facebook redirects user back to /auth/facebook/callback with an authorization code in tow, the server exchanges this for an access token and some information about the user
Server finds or creates the user in the DB using the info, then creates a JWT containing a relevant subset of the user data (e.g. ID)
???
At this point I just want the user to be redirected to the main page for the React app (let's say /app) with the JWT in tow, so the frontend can take over. But I can't think of an (elegant) way to do that without losing the JWT along the way, other than to put it in the query string for the redirect (/app?authtoken=...) - but that will display in the address bar until I remove it manually using replaceState() or whatever, and seems a little weird to me.
Really I'm just wondering how this is typically done, and I'm almost sure I'm missing something here. The server is Node (Koa with Passport), if that helps.
Edit: To be clear, I'm asking what the best way is to provide a token to the client (so it can be saved) after an OAuth redirect flow using Passport.
I recently ran across this same issue, and, not finding a solution here or elsewhere, wrote this blog post with my in-depth thoughts.
TL;DR: I came up with 3 possible approaches to send the JWT to the client after OAuth logins/redirects:
Save the JWT in a cookie, then extract it on the front-end or server in a future step (eg. extract it on the client with JS, or send a request to the server, server uses the cookie to get the JWT, returns the JWT).
Send the JWT back as part of the query string (which you suggest in your question).
Send back a server-rendered HTML page with a <script> tag that:
Automatically saves the embedded JWT to localStorage
Automatically redirects the client to whatever page you like after that.
(Since logging in with JWTs is essentially equivalent to "saving the JWT to localStorage, my favorite option was #3, but it's possible there are downsides I haven't considered. I'm interested in hearing what others think here.)
Hope that helps!
Client: Open a popup window via $auth.authenticate('provider name').
Client: Sign in with that provider, if necessary, then authorize the application.
Client: After successful authorization, the popup is redirected back to your app, e.g. http://localhost:3000, with the code (authorization code) query string parameter.
Client: The code parameter is sent back to the parent window that opened the popup.
Client: Parent window closes the popup and sends a POST request to /auth/provider withcode parameter.
Server: Authorization code is exchanged for access token.
Server: User information is retrived using the access token from Step 6.
Server: Look up the user by their unique Provider ID. If user already exists, grab the existing user, otherwise create a new user account.
Server: In both cases of Step 8, create a JSON Web Token and send it back to the client.
Client: Parse the token and save it to Local Storage for subsequent use after page reload.
Log out
Client: Remove token from Local Storage
here is a login request from the server side. it's storing the token in the header:
router.post('/api/users/login', function (req, res) {
var body = _.pick(req.body, 'username', 'password');
var userInfo;
models.User.authenticate(body).then(function (user) {
var token = user.generateToken('authentication');
userInfo = user;
return models.Token.create({
token: token
});
}).then(function (tokenInstance) {
res.header('Auth', tokenInstance.get('token')).json(userInfo.toPublicJSON());
}).catch(function () {
res.status(401).send();
});
});
here is the login request on the react side, where I am grabbing the token from the header and setting the token in local storage once the username and password pass authentication:
handleNewData (creds) {
const { authenticated } = this.state;
const loginUser = {
username: creds.username,
password: creds.password
}
fetch('/api/users/login', {
method: 'post',
body: JSON.stringify(loginUser),
headers: {
'Authorization': 'Basic'+btoa('username:password'),
'content-type': 'application/json',
'accept': 'application/json'
},
credentials: 'include'
}).then((response) => {
if (response.statusText === "OK"){
localStorage.setItem('token', response.headers.get('Auth'));
browserHistory.push('route');
response.json();
} else {
alert ('Incorrect Login Credentials');
}
})
}
When you get a token from any passport authentication sites you have to save the token in your browser's localStorage. The Dispatch is Redux's Middleware. Ignore dispatch if you don't use redux in your app. you can just use setState here (A bit weird without redux).
Client-side:
Here's something similar API of mine, which returns token.
saving tokens
axios.post(`${ROOT_URL}/api/signin`, { email, password })
.then(response => {
dispatch({ type: AUTH_USER }); //setting state (Redux's Style)
localStorage.setItem('token', response.data.token); //saving token
browserHistory.push('/home'); //pushes back the user after storing token
})
.catch(error => {
var ERROR_DATA;
try{
ERROR_DATA = JSON.parse(error.response.request.response).error;
}
catch(error) {
ERROR_DATA = 'SOMETHING WENT WRONG';
}
dispatch(authError(ERROR_DATA)); //throw error (Redux's Style)
});
So When you make some authenticated requests,you have to attach the token with the request in this form.
authenticated requests
axios.get(`${ROOT_URL}/api/blog/${blogId}`, {
headers: { authorization: localStorage.getItem('token') }
//take the token from localStorage and put it on headers ('authorization is my own header')
})
.then(response => {
dispatch({
type: FETCH_BLOG,
payload: response.data
});
})
.catch(error => {
console.log(error);
});
Here's my index.js:
The token is checked each and everytime, so even if the browser got refreshed, you can still set the state.
checks if the user is authenticated
const token = localStorage.getItem('token');
if (token) {
store.dispatch({ type: AUTH_USER })
}
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
..
..
..
<Route path="/blog/:blogid" component={RequireAuth(Blog)} />
//ignore this requireAuth - that's another component, checks if a user is authenticated. if not pushes to the index route
</Route>
</Router>
</Provider>
, document.querySelector('.container'));
All that dispach actions does is it sets the state.
my reducer file(Redux only) else you can just use setState() in your index route file to provide the state to the whole application. Every time the dispatch is called, it runs a similar reducer file like this which sets the state.
setting the state
import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types';
export default function(state = {}, action) {
switch(action.type) {
case AUTH_USER:
return { ...state, error: '', authenticated: true };
case UNAUTH_USER:
return { ...state, error: '', authenticated: false };
case AUTH_ERROR:
return { ...state, error: action.payload };
}
return state;
} //you can skip this and use setState() in your index route instead
Delete the token from your localStorage to logout.
caution: Use any different name rather than token to save the token in your browser's localStorage
Server-Side:
considering your passport services file. You must set the header search.
Here's passport.js
const passport = require('passport');
const ExtractJwt = require('passport-jwt').ExtractJwt;
const JwtStrategy = require('passport-jwt').Strategy;
..
..
..
..
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'), //client's side must specify this header
secretOrKey: config.secret
};
const JWTVerify = new JwtStrategy(jwtOptions, (payload, done) => {
User.findById(payload._id, (err, user) => {
if (err) { done(err, null); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
passport.use(JWTVerify);
In my router.js
const passportService = require('./services/passport');
const requireAuthentication = passport.authenticate('jwt', { session: false });
..
..
..
//for example the api router the above react action used
app.get('/api/blog/:blogId', requireAuthentication, BlogController.getBlog);

Firebase 3.0 Tokens : [Error: Invalid claim 'kid' in auth header.]

I'm trying to create JWT tokens in node.js for use with the REST api in firebase, but when I try to use them, I get the error "Error: Invalid claim 'kid' in auth header."
This is my code
http.createServer(function (req, res) {
var payload = {
uid: "bruh"
};
var token = jwt.sign(payload, sact["private_key"], {
algorithm: 'RS256',
issuer: sact["client_email"],
subject: sact["client_email"],
audience: 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit',
expiresIn: '3600s',
jwtid: sact["private_key_id"],
header: {
"kid": sact["private_key_id"]
}
});
res.writeHead(200);
res.end("It worked. (" + token + ")");
}).listen(port);
These are my requires
var http = require('http');
var jwt = require('jsonwebtoken');
Please use returnSecureToken: true, with correct Spellings
I hope it will solve the problem of Invalid claim 'kid' in the auth header.
This is an issue because you're generating a Firebase ID token, not an access token for the Firebase REST API.
To generate a REST API token I would use the legacy Firebase Token Generator library which still works perfectly well (but only generates REST tokens, not general purpose access tokens).
Note that your Firebase Database secret is now located under the gear icon in the top left of the console.
So I had this error and I've fixed it. Now here is the solution:
You'll need to retrieve the ID-token using an additional function. Here is the function you can use:
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
I implemented it somewhat like this:
//google OAuth login handler
const googleLoginHandler = () => {
const provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
setgoogleAuthStatus(true)
// The signed-in user info.
const userId = result.user.uid;
const displayName = result.user.displayName;
const email = result.user.email;
//This is the function for getting the ID-Token
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then((idToken) => {
// Send token to your backend via HTTPS
console.log(idToken)
}).catch((error) => {
// Handle error
console.log(error.message)
alert(error.message)
});
console.log(result)
}).catch((error) => {
console.log(error)
// Handle Errors here.
alert(error.message)
})
}
The id token you get by this method can be used to access the firebase real-time database and other firebase services.
check out these links for more details:
https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients
https://firebase.google.com/docs/database/rest/auth#firebase_id_tokens

Resources