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

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.

Related

The account does not exist in this organization error during Microsoft SSO

I am trying to do a microsoft sso, where any user can use his microsoft account to sign in to the app. Now, if the user is part of the application users it works perfectly fine, but when I try to log in with my personal one, it gives me an error message saying "The account does not exist in this organization".
My endpoint for the sso:
const REDIRECT_URI = "http://localhost:3000/redirect";
const cca = new msal.ConfidentialClientApplication(config);
const scopes = ["user.read"];
router.get("/auth", (req, res) => {
// Construct a request object for auth code
const authCodeUrlParameters = {
scopes: scopes,
redirectUri: REDIRECT_URI,
};
// Request auth code, then redirect
cca
.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
return res.send(response);
})
.catch((error) => res.send(error));
});
router.get("/redirect", (req, res) => {
// Use the auth code in redirect request to construct
// a token request object
const tokenRequest = {
code: `${req.query.code}`,
scopes: scopes,
redirectUri: REDIRECT_URI,
};
// Exchange the auth code for tokens
cca
.acquireTokenByCode(tokenRequest)
.then(async (response) => {
res.send(response);
})
.catch((error) => res.status(500).send(error));
});
It gives me an error message saying, "The account does not exist in this organization".
The error message shows that your personal Microsoft account is not associated with the organization that the application is using for authentication. Microsoft provides two different authentication services: Microsoft Account (MSA) and Azure Active Directory (AAD).
By using Guest method, you can use your personal account by assigning the roles.
If your organization has set up the application to use AAD for authentication, then you need to have an AAD account in the same organization in order to log in.
Create an AAD account and map it with the same email address as your MSA. This will allow you to log into the application using your AAD account.
And if you don't want to create an AAD account, you can use a different email address (such as a work email address) that is already associated with the organization's AAD.
Steps to create Microsoft SSO
Register your application to access Microsoft Graph API, and you need to first register your application in the Microsoft Developer Portal and obtain a client ID and secret.
The first step in the authorization code grant flow is to redirect the user to the Microsoft authorization endpoint. The user must sign in and grant the permissions that your application requests.
After the user grants permission, Microsoft will redirect the user back to your application with an authorization code.
Your application can then exchange this code for an access token by making a POST request to the Microsoft token endpoint.
Once you have an access token, you can use it to call the Microsoft Graph API on behalf of the user. To do this, you'll need to add the access token to the Authorization header of your API request.
Access tokens are short-lived and need to be refreshed after a certain time period. You can use the refresh token to obtain a new access token without requiring the user to sign in again.
async function getAccessToken(code) {
const tokenRequestBody = querystring.stringify({
grant_type: 'authorization_code',
code: code,
redirect_uri: redirectUri,
client_id: clientId,
client_secret: clientSecret,
});
const tokenResponse = await axios.post('https://login.microsoftonline.com/common/oauth2/v2.0/token', tokenRequestBody);
return tokenResponse.data.access_token;
}
async function getUserProfile(accessToken) {
const userProfileResponse = await axios.get('https://graph.microsoft.com/v1.0/me', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return userProfileResponse.data;
}
async function main() {
const code = 'AUTHORIZATION_CODE';
const accessToken = await getAccessToken(code);
const userProfile = await getUserProfile(accessToken);
console.log(userProfile);
}
References taken from
Nodejs-webapp-msal
ADD

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.

(msal-node) - Dynamics 365 Business Central returns 401 for any resource with received token

We are currently switching to an OAuth based authorization solution based on the #azure/msal-node package to authorize our API's with the ones provided by Dynamics 365 Business Central (19.5, Cloud). Somehow I can not get it to work. Our tenant instance just returns a 401 when requesting any resource using the retrieved token.
Really reached a dead end here, any help would be greatly appreciated.
Permissions set in Azure's "App Registrations" section
And yes, I have consented all of those Permissions on behalf of our tenant (admin consent)
API.ReadWrite.All
app_access
Automation.ReadWrite.All
The token even contains following scopes when decoded over at jwt.ms:
{
"scopes": [
"Automation.ReadWrite.All",
"app_access",
"API.ReadWrite.All"
]
}
Example of our implementation
/**
* Uses `msal-node` to authenticate against the microsoft servers
* to gain access to Dynamics 365 Business Central
*/
async getClientCredentialsToken() {
try {
const validity = await this.validateClientCredentialsToken();
if (validity) return;
const authOptions = {
clientId: process.env.AAD_CLIENT_ID,
authority: process.env.AAD_AUTHORITY,
clientSecret: process.env.AAD_CLIENT_SECRET,
};
const cacheOptions = {
Account: {},
IdToken: {},
AccessToken: {},
RefreshToken: {},
AppMetadata: {},
};
const cca = new msal.ConfidentialClientApplication({
cache: cacheOptions,
auth: authOptions,
});
const response = await cca.acquireTokenByClientCredential({
azureRegion: null,
skipCache: true,
scopes: ["https://api.businesscentral.dynamics.com/.default"],
});
return response;
} catch (err) {
console.log(err);
}
},
Example of a response when requesting an existing endpoint
No matter what endpoint is hit. This is the response I get for it:
{
"error": {
"code": "Authentication_InvalidCredentials",
"message": "The server has rejected the client credentials. CorrelationId: b004d293-f576-40c9-bbc6-3fb32533a65b."
}
}
Solution found inside the Docs
So for anybody stumbling upon the same scenario I have found something that finally worked! You need to register the application inside the Dynamics 365 Business Central client as described inside the official documentation.

Teams bot SSO without dialogs framework. Invalid x5t claim

I'm following this tutorial:
https://learn.microsoft.com/en-us/learn/modules/msteams-sso/7-exercise-bots-sso
https://github.com/OfficeDev/TrainingContent/tree/master/Teams/80%20Using%20Single%20Sign-On%20with%20Microsoft%20Teams/Demos/02-learn-msteams-sso-bot
https://youtu.be/cmI06T2JLEg
https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/24.bot-authentication-msgraph
The bot worked as expected. But I would like not to use the dialog framework. I'm having trouble adapting the model.
In the personal scope I reply to a message with an oauth card:
const oauthCard = await CardFactory.oauthCard(SsoConnectionName, undefined, undefined, undefined, {
id: 'random65jHf9276hDy47',
uri: `api://botid-${MicrosoftAppId}`
})
await context.sendActivity(MessageFactory.attachment(oauthCard))
so i get the token on the handler
handleTeamsSigninTokenExchange(context, query) {
if (context?.activity?.name === tokenExchangeOperationName) {
console.dir(context?.activity?.value)
token = context?.activity?.value?.token
}
}
What am I supposed to do with this token? I get the Invalid x5t claim error when I try to use microsoft client like this:
msGraphClient = microsoft.Client.init({
debugLogging: true,
authProvider: done => {
done(null, token)
}
})
// on message 'whoiam'
const me = await msGraphClient.api("me").get()
Is this the correct token? How do I initialize the Microsoft Graph client with this token?
My repo sample: https://github.com/itacirgabral/teamsbotSSOdemo/blob/nodialog/nodialogs.js
You can use below code snippet for initializing the Graph Client:
// Get an Authenticated Microsoft Graph client using the token issued to the user.
this.graphClient = Client.init({
authProvider: (done) => {
done(null, this._token); // First parameter takes an error if you can't get an access token.
}
});
Refence sample link:
https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-sso/nodejs/server/models/simpleGraphClient.js
https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-sso/nodejs/server/api/appController.js

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

Resources