How to resolve Nodejs google-auth-library invalid token signature error? - node.js

I'm using flutter for my mobile app. I try to add sign in with google. Everything is okay for Flutter side. I'm gettin idToken from mobile app and send to my backend, nodejs.
Now, I want to use this idToken to authenticate user's requests on nodejs backend side with google-auth-library package.
let token = "token"
const CLIENT_ID = "client_id"
const { OAuth2Client } = require('google-auth-library');
const client = new OAuth2Client(CLIENT_ID);
async function verify() {
try {
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
});
const payload = ticket.getPayload();
const userid = payload['sub'];
console.log(payload)
} catch (error) {
console.log(error)
}
}
verify()
But this code always returns this error => Error: Invalid token signature:
at OAuth2Client.verifySignedJwtWithCertsAsync (\node_modules\google-auth-library\build\src\auth\oauth2client.js:566:19)
What should I do for to verify this idToken on nodejs backend side?
Thanks.

If the idToken that you are passing to the function is from the log of your flutter app, it is likely that you are not getting the entire idToken printed in the log due to the limitations of print().
I used the below code snippet to print out the idToken and used that in the API which gave me a success response.
print('ID TOKEN');
String token = googleAuth.idToken;
while (token.length > 0) {
int initLength = (token.length >= 500 ? 500 : token.length);
print(token.substring(0, initLength));
int endLength = token.length;
token = token.substring(initLength, endLength);
}

Related

Required claim nbf not present in token (using Firebase Microsoft Signin trying to access MicrosoftGraph)

I currently have an app with the following structure: Angular front-end, Node.js server.
We have implemented Google Cloud's Identity Providers to sign in using Google and/or Microsoft.
Google Sign-in and access the Google Cloud Admin SDK working perfectly, however trying to access Microsoft Graph is giving the following error:
UnhandledPromiseRejectionWarning: Error: Required claim nbf not present in token
According to Firebase documentation you can use the access token recieved from the Signin to access Graph:
Firebase documentation screenshot
On successful completion, the OAuth access token associated with the provider can be retrieved from the firebase.auth.UserCredential object returned. Using the OAuth access token, you can call the Microsoft Graph API. For example, to get the basic profile information, the following REST API can be called:
curl -i -H "Authorization: Bearer ACCESS_TOKEN" https://graph.microsoft.com/v1.0/me
When running the above in url I get the same error:
{"error":{"code":"InvalidAuthenticationToken","message":"Required claim nbf not present in token","innerError":{"date":"2022-05-26T12:51:11","request-id":"##########","client-request-id":"##########"}}}
I'm using the signInWithPopup in my authentication service (authentication.service.ts):
await signInWithPopup(this.auth, provider)
.then((result) => {
const credential = OAuthProvider.credentialFromResult(result);
const accessToken = credential.accessToken;
const idToken = credential.idToken;
this.setCurrentUser(result.user);
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
const email = error.email;
const credential = OAuthProvider.credentialFromError(error);
console.log(error);
});
I send the accessToken to my server (app.ts):
app.get(
'/api/microsoft-get-organisation',
async (req: express.Request, res: express.Response) => {
//https://graph.microsoft.com/v1.0/organization
const organisation = await ms.getOrganisation(req.headers, '/organization');
res.send(JSON.stringify(organisation));
}
);
export const getOrganisation = async (headers: any, graphEndpoint: string) => {
const client = await getAuthenticatedClient(headers);
return await client.api(graphEndpoint).get();
};
async function getAuthenticatedClient(headers: any) {
const client = await graph.Client.init({
authProvider: (done: any) => {
done(null, headers.authorization.split(' ')[1]);
},
});
return client;
}
When verifying the token I can see that there is no nbf claim:
token screen shot
Any advice on what I have done wrong so that I can access Microsoft Graph?

How to verify Google signin (via Firebase) idToken in nodejs backend?

Trying to verify idToken of a user signed in via firebase authentication (Google signin) in nodejs server. Server throws Firebase ID token has invalid signature.
Tried verifying with firebase-admin as well as jsonwebtoken with public key from the url: https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com. Both methods work perfect for users signed in with a password, but throws 'Invalid Signature' in case of a user signed in via google.
Is there anything I am doing wrong? Do I need to verify with google-auth-library instead?
Code:
import * as admin from "firebase-admin";
admin.initializeApp({
credential: admin.credential.cert(require("../../serviceAccount")), // file received from firebase project settings page
databaseURL: "as mentioned in the firebase project settings page",
});
// Some code here
var token = "token received from client side";
var decoded = await admin.auth().verifyIdToken(token);
PS:
All client side features (after signing in) are working fine.
Everything else on the backend is working fine.
Decoding the token in both cases gives expected JSON.
For test run, token is being forceRefreshed everytime before calling the API.
OP here,
I am dumb.
I was using the print() function of flutter to log the token and call the API myself. Didn't know Flutter's print function has an output character limit. Login using password gives smaller tokens thus the whole token was logged. But Google sign in gives a longer token, longer than the output character limit of print.
Solution : Use log function from 'dart:developer' package.
import 'dart:developer';
//
log(await _auth.idToken);
const { OAuth2Client } = require("google-auth-library");
const client = new OAuth2Client(googleClient[process.env.ENV])
let token = 123456789011-crhch2kuum79bk0qr3usa39f7b9chikc.apps.googleusercontent.com
async function googleLoginVerify(token) {
try {
const ticket = await client.verifyIdToken({
idToken: token,
audience: googleClient[process.env.ENV],
});
const payLoad = ticket.getPayload();
return {
success: true,
data: payLoad,
};
} catch (err) {
console.log(err.message);
return {
success: false,
message: err.message,
};
}
}

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
};

Auth0 "service not found" error

I'm attempting to use Auth0 to issue JWT tokens for accessing my API (so that Auth0 handles all the OAuth and security concerns, etc., and my API just needs to check the token). When I try to test the Authorization Code flow for clients to receive an access token (using Node + Express), the following happens:
The authorization code request works fine, and the client is redirected back to my redirect_uri with the code appended to the query. All good.
The token request then always fails. If I include the audience parameter, the request returns an access_denied error with the following details: Service not found: {the audience parameter}, regardless of what value I set for the audience parameter.
If I don't include the audience parameter, I get a server_error with the message Service not found: https://oauth.auth0.com/userinfo.
I've checked every Auth0 setting and read every documentation page thoroughly, and so far nothing has worked. I've also tested the Authorization Code flow in Auth0's API debugger, and it worked fine. My test follows exactly the same parameters, and yet still receives an error requesting the token. I'm testing on localhost. The client credentials and implicit flows are working fine.
Here is a test endpoint I created which retrieves the authorization code from Auth0:
const qs = require('querystring');
const getCode = (req, res) => {
const params = {
audience, // the value of the API Audience setting for the client
client_id, // the client ID
redirect_uri, // the redirect_uri, which is also listed in the Allowed Callback URLs field
response_type: `code`,
scope: `offline_access open` // ask to return ID token and refresh token,
state: `12345`,
};
const authDomain = `mydomain.auth0.com/oauth`;
res.redirect(`${authDomain}/oauth/authorize?${qs.stringify(params)}`);
};
The redirect_uri then redirects to the following endpoint, where I make the request for the access token:
const https = require('https');
const callback = (req, res) => {
const body = {
client_id,
client_secret,
code: req.query.code,
grant_type: `authorization_code`,
redirect_uri, // same value as provided during the code request
};
const opts = {
headers: { 'Content-Type': `application/json` },
hostname: `mydomain.auth0.com`,
method: `POST`,
path: `/oauth/token`,
};
const request = https.request(opts, response => {
let data = ``;
response.on(`data`, chunk => { data += chunk; });
response.on(`error`, res.send(err.message));
response.on(`end`, () => res.json(JSON.parse(data))); // this executes, but displays the error returned from Auth0
});
request.on(`error`, err => res.send(err.message));
request.end(JSON.stringify(body), `utf8`);
};
Any suggestions as to what I might be doing wrong?
The issue was that I was calling the incorrect URL at Auth0. I mistakenly thought that both the authorization and token endpoints began with /oauth, when in fact the authorization endpoint is just /authorize, while the token endpoint is /oauth/authorize. Correcting the URLs in my code fixed the problem.
My solution was the identifier of the api was not found. If it is not exact it won't find it. I had an extra backslash on my 'audience' where the identifier didnt have one. pretty easy mistake but the error is not very clear in Auth0.
In my case, I was using auth0 react hooks. So the example code looked like this:
const getUserMetadata = async () => {
const domain = process.env.REACT_APP_AUTH0_DOMAIN
try {
const accessToken = await getAccessTokenSilently({
audience: `https://${domain}/api/v2/`,
scope: 'read:current_user',
})
console.log('accessToken', accessToken)
localStorage.setItem('access_token', accessToken)
setUserAuthenticated(true)
} catch (e) {
console.log('error in getting access token', e.message)
}
}
My solution to this was using by default Auth0 Audience value in audience field
const getUserMetadata = async () => {
const auth0audience = process.env.REACT_APP_AUTH0_AUDIENCE
try {
const accessToken = await getAccessTokenSilently({
audience: auth0audience,
scope: 'read:current_user',
})
console.log('accessToken', accessToken)
localStorage.setItem('access_token', accessToken)
setUserAuthenticated(true)
} catch (e) {
console.log('error in getting access token', e.message)
}
}
Because its stated in auth0 docs of configuring custom domains that, you need to use by default API audience
Source - https://auth0.com/docs/brand-and-customize/custom-domains/configure-features-to-use-custom-domains

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