Unable to verify Azure AD token with NestJS application - node.js

I'm trying to verify Azure AD token with my NestJS backend application. I'm logging to Azure AD using React frontend application and, for now, grab access_token from the response manually. Then I have this guard in NestJS:
#Injectable()
export class AzureADStrategy extends PassportStrategy(
BearerStrategy,
'azure-ad',
) {
constructor() {
super({
identityMetadata: `https://login.microsoftonline.com/${tenantID}/v2.0/.well-known/openid-configuration`,
clientID,
clientSecret,
loggingLevel: 'debug',
loggingNoPII: false
});
}
async validate(response: any) {
console.log(response);
}
}
export const AzureGuard = AuthGuard('azure-ad');
When i apply it on some endpoint i'm trying to fetch this URL, like:
curl localhost:9000/test --header 'Authorization: Bearer xyz'
But i'm not able to authenticate and i get this error log:
{"name":"AzureAD: Bearer Strategy","hostname":"<hostname>","pid":1713974,"level":30,"msg":"authentication failed due to: invalid signature","time":"2022-11-03T13:00:51.213Z","v":0}
How should i configure it to make it work?

I'm assuming you've been able to login ok and then pass the details to the API and you're user/s are registered under the APP.
On validate, this is what i have and works fine.
async validate(data) {
return data;
}
This is the example i followed to get it working - https://medium.com/adidoescode/azure-ad-for-user-authentication-with-vue-and-nestjs-4dab3e96d240

Related

NodeJS Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential

I am trying Google Apps Script API. I receive this
Request is missing required authentication credential" error by trying service.scripts.run({ // params }).
Can somebody help me figure out, how to authenticate credentials?
I am using google.auth.OAuth2() for my authentication.
This is my code:
const service = google.script({ version: 'v1', auth });
await service.scripts.run({ scriptId, requestBody: { function: 'myFunction', devMode: true }});
service.scripts is already working fine with these methods:
await service.projects.create({ // params })
and
service.projects.updateContent({ // params })
Here are my scopes:
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/script.projects
https://www.googleapis.com/auth/script.scriptapp
I am expecting for the my Apps Script to run programmatically.

AuthenticationFailed when authenticating via nodejs app and package #azure/msal-node

I have an Azure app registered . I am trying to authenticate to that app . I am able to do that and successfully get the accesstoken and idtoken.
However, when I use that token and try to make a request to list subscriptions API (https://management.azure.com/subscriptions?api-version=2020-01-01) , the request fails and give response "AuthenticationFailed". I have also tried changing the scope to https://management.azure.com/.default but the same error is there. Below is the nodejs code and I am also attaching the API permissions of app
const config = {
auth: {
clientId: 'xxx',
authority: 'https://login.microsoftonline.com/organizations',
clientSecret: 'yyy',
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
},
},
};
// Create msal application object
const pca = new msal.ConfidentialClientApplication(config);
// Create Express App and Routes
const app = express();
app.get('/', (req, res) => {
const authCodeUrlParameters = {
scopes: ['user.read','https://management.azure.com/user_impersonation'],
redirectUri: REDIRECT_URI,
};
// get url to sign user in and consent to scopes needed for application
pca
.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
res.redirect(response);
})
.catch((error) => console.log(JSON.stringify(error)));
});
The response I am getting is
{
"error": {
"code": "AuthenticationFailed",
"message": "Authentication failed."
}
}
The error "AuthenticationFailed" usually occurs if you are using different scope token to call the API.
I tried to generate access token with the same scope as you
via Postman and got the same error while calling the query like below:
Please note that,
user.read audience is Microsoft Graph API
https://management.azure.com/user_impersonation audience is Azure Service Management.
As you have given two different scopes with different audiences, it will consider the first scope (user.read) to generate the token as mentioned in this SO Thread which was solved by me.
When you call the query https://management.azure.com/subscriptions?api-version=2020-01-01 with the above token, you will get the error as it is intended for MS Graph audience.
I tried to generate the token with scope https://management.azure.com/user_impersonation only, removing user.read like below:
With the above generated token, I am able to call the API successfully like below:
If you want token with different scopes, then you have to generate two access tokens separately.

external api handling in the backend

On my express server I make queries to an external API using its own token. When I log in to my server I request a token to the external API based on the user who logged in, and I keep the token of the external API in the token of my express server.
Each user gets different data according to their token from the external api, for queries that require external API information, I read the received token and get the external API token to send it through headers with axios, for example:
const LoginUser = (request, response) {
axios.post('/ExternalApi/auth',request.body)
.then( data =>{
const payload = {
...
tokenExternalApi: data.token
}
const token = jwt.sign(payload, ...)
return response.status(200).json(token)
})
}
const getData = (req, response){
const tokenFromClient = req.headers.authorization
//Function extract tokenExternalApi from payload Token
const tokenExternalApi = getTokenExternl(tokenFromClient )
axios.get(`/urlExternalApi`, { headers:
{ Authorization: tokenExternalApi }}
).then(res => {
return response.status(200).json(res.data)
})
}
Is this the correct approach to managing external apis tokens or is there a cleaner way to do it?
Here is my sample code that I use for hit an external API within function in node js using axios
first time you should install axios npm install axois
const axios = require('axios');
async yourFunction(){
axios({
method: 'POST',
url: "http://yoururl.com",
data: {
name: '+62'+phoneNumber,
number: '+62'+phoneNumber,
message: 'success',
}
});
}
In my personal opinion, this seems to be a clean approach.
But keep in mind that tokens are visible to users, so the fact is your users can decode the token, view tokenExternalApi, know that you are using an external API in the backend and directly make calls to ExternalApi using that token, provided they have the know-how of it. If you understand this fact and are fine with it, then this works.
Otherwise, you can consider encoding the token before sending it to the user or store it on the server-side session.

How does Passport (oAuth2) work with GraphQL (TypeGraphQL)?

I am attempting to use the Google Strategy.
My goal is to have the user access to the restricted area of the React Next.js app by being authenticated only through the Google Sign in. This is based off the extension of their email being #SpecificDomain.com.
I have a component on the frontend that opens up a Google sign in window and returns the object with the access token. As I understand, I am then to forward this object to the my own backend (Apollo server, TypeGraphQL config).
My ApolloServer instance looks like this:
onst apolloServer = new ApolloServer({ schema,
context: async ({ req}) => {
let token = null;
let currentUser = null;
try{
token = req.headers.authorization;
if(token){
currentUser = await authenticate(token);
}
} catch (error) {
console.warn(`Unable to authenticate using auth token: ${token}`);
}
return {
currentUser,
token
}
} }) as any;
From inside the context I am attempting to verify the users access token by sending the token to another component where I am trying to use Passport which uses the Google oauth2 strategy to verify the user.
However, all the examples I see online are using Express middleware. I am wondering if there's any way to do it with just GraphQL?
I do not understand how I am meant to call this:
passport.use(new GoogleStrategy({
clientID: googleCredentials.CLIENT_ID,
clientSecret: googleCredentials.CLIENT_SECRET,
callbackURL: googleCredentials.redirect_uris[0]
},
function(accessToken : string, cb : any, refreshToken? : string, profile? : string) {
const userOject = {accessToken, refreshToken, profile};
return cb(null, userOject);
}
));
How do I pass a token into this?
I tried this too,
export default function authenticateUser(token : string) {
passport.authenticate('oauth2');
}
But passport.authenticate doesn't have any token parameters.
Could someone please give me some pointers or point the right way?
Thank you for your time.
Settled with using Express to handle authentication.

How to create Firebase token on server for use with unit tests?

I need to authenticate a Firebase user using node so I can test some server side methods. For each protected request, I verify the Firebase token using:
firebase.auth().verifyIdToken(firebaseAccessToken).then(function(decodedToken) {
// forward request
})
So in my test I created a token with a uid from my Firebase database
firebase.auth().createCustomToken(uid).then(function(token) {
//add to header for requests
})
Later I read that custom tokens are not verified by the verifyIdToken method, only client generated ones.
I've looked at this answer - server side verification of tokens in firebase
So I added databaseAuthVariableOverride to the init json
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: [dbURL],
databaseAuthVariableOverride: {
uid: [uid]
}
});
Still getting the output in my tests
Error: expected 200 "OK", got 401 "Unauthorized"
And the firebase error -
Error: Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
So how do I emulate a user with my current setup?
Here's a Python script for generating Firebase ID tokens (not custom tokens).
python firebase_token_generator.py <UID>
There are probably easier ways to do this but you could call the Python script from Node.
You can generate a Firebase Id token from your custom token, then use that for verification. Eg:
const rp = require("request-promise");
// 'customToken' comes from FirebaseAdmin.auth().createCustomToken(uid)
function getIdTokenFromCustomToken(customToken) {
const url = `https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=${API_KEY}`;
const data = {
token: customToken,
returnSecureToken: true
};
var options = {
method: "POST",
uri: url,
body: data,
json: true // Automatically stringifies the body to JSON
};
return rp(options)
// idToken is the firebase id token that can be used with verifyIdToken
.then(parsedBody => parsedBody.idToken)
.catch(function(err) {
// POST failed...
});
}

Resources