When switching from adal to the msal 2.0 browser library, I'm missing the UPN of the user in the idToken response, which leads to a principal that doesn't have a name in principal.Identity.Name after passing the idToken to the backend. This was available in the adal version.
var principal = tokenHandler.ValidateToken(validationToken, validationParameters, out SecurityToken validatedToken);
I get preferred_name as a claim, but it doesn't seem to be usable to call the Graph API to retrieve an email address, as it's not a UPN.
How do I need to change token validation and UPN retrieval for msal in general? Pass the accessToken and validate that separately to get more claims?
Docs for idToken
Docs for accessToken
Code sample
Turns out msal supports 2 ways to get additional claims
Via AD manifest settings
When requesting a token as below
by adding additional scopes
const loginRequest = {
scopes: ['User.Read', 'email']
};
and when validating a token different claims can be used to get the principal's identity by setting TokenValidationParameters.NameClaimType
Related
When locally trying to use Google books api to get public data:
Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project."
API_URL = "https://www.googleapis.com/books/v1/volumes";
this.http
.get<GoogleBooksApiInterface>(`${this.API_URL}?q=${title}`)
Even after adding &key=api_key, the response is the same.
After spent some time to figure it out and found solution (which is not unfortunately mentioned in google documentation):
to make it works in my case I use NodeJS on the backend side and moved there this API request.
Angular => NodeJS => API call => Angular
I'm creating a simple web app that needs to call an Azure Function.
I've registered 2 apps in my Azure AD: one for my single page react application and another for my Azure Function.
The app for my azure function has an API exposed on it and a scope configured called CallApi.
The app itself has authentication configured on it (easy auth), and the client id matches the API app registration within AAD:
The app for my SPA has permission to request this scope and this is granted by default for all users.
I am able to successfully log users into my SPA and get an access token. I'm doing this with the use of the #azure/msal-browser and #azure/msal-react npm libraries, specifically:
<MsalAuthenticationTemplate interactionType={InteractionType.Redirect} authenticationRequest={{scopes: ["User.Read", "api://<redacted>/CallApi"]}} >
However, the access token that is returned only has these scopes: ['openid', 'profile', 'User.Read', 'email'] and not api://<redacted>/CallApi. So when I try to exchange my access token for a token by sending a POST request to https://<app_name>.azurewebsites.net/.auth/login/aad it returns an unauthorized message.
How can I ensure that the CallApi scope is allow by my app?
So when I try to exchange my access token for a token by sending a POST request to https://<app_name>.azurewebsites.net/.auth/login/aad it returns an unauthorized message.
Please ensure that the URL is correct as it should be in the format of <app-url>/.auth/login/aad/callback and note that the parameters are in the body of the HTTP POST request.
However, the access token that is returned only has these scopes: ['openid', 'profile', 'User.Read', 'email']
openid
If an app performs sign-in by using OpenID Connect, then it must request the openid scope. That is to say, the openid scope displays on the work account consent page as the “Sign you in” permission.
email
The email scope can work with the openid scope. As it gives the app access to the user’s primary email address in the form of the email claim. Where, the email claim is included in a token only if an email address is associated with the user account, which isn’t always the case.
profile
The profile scope can also work with the openid scope. This gives the app access to a substantial amount of information about the user. The information that it has access includes, the user’s given name, surname, preferred username, and object ID.
In the Microsoft ecosystem there are some high-privilege permissions that can be set to admin-restricted. This include:
Firstly, read all user’s full profiles by using User.Read.All
Secondly, write data to an organization’s directory by using Directory.ReadWrite.All
Lastly, read all groups in an organization’s directory by using Groups.Read.All
Please refer this article for an example regarding the POST request regarding the Call API Scope.
In Our application uses AAD for authenticating the corporate users within our organization only.
Is that possible to set the response type as access token instead of default id_token ? Here is my config.
// const msalConfig = {
// auth: {
// clientId: configuration.msal.clientId,
// tenantId:configuration.msal.tenantId,
// redirectUri: window.location.href
// },
// cache: {
// cacheLocation: 'sessionStorage',
// storeAuthStateInCookie: true
// }
// };
I get the error as but if i enable id_token in authentication under Azure App registrations authentication it works again.. but is that possible to get the authentication done just with access token enabled.
msal handleRedirectCallback ServerError: AADSTS700054: response_type 'id_token' is not enabled for
the application.
T
MSAL.JS is used implement browser level flows like
implicit flow.
Id token is required for implicit flow to validate that a user is
who they claim to be and get additional useful information about
Then for security purpose.
In implicit flow we need to first authorize with id token and then only we can request for access token.
Implicit flow is recommended only if the application has a single-page architecture (SPA), has no back-end components.
You are facing the error because implicit flow is not enabled.
To enable implicit flow please check Access tokens and ID tokens in your registered application as mentioned below
Do you mean you want to use Authorization Code Flow ? If so, you have to use MSAL v2.
EDIT: sorry, I misread the question.
You cannot directly ask for an access token with MSAL, you have to login first (id_token) and then acquire a token (access_token).
This is all explained in the docs.
I wanted to know if it was possible possible to use a service account to request data from any google API but using this library: https://github.com/google/google-api-javascript-client
I managed to find out how to use the library with OAuth2.0 credential from google cloud console.
But my real need requires me to use a service account to fetch these data.
Here's the code I used to fetch data from OAuth2.0 credentials:
initClient() {
return gapi.client.init({
apiKey: this.GSC_API_KEY, // already defined in the application
client_id:
"xxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
scope:
"https://www.googleapis.com/auth/webmasters https://www.googleapis.com/auth/webmasters.readonly",
discoveryDocs: [
"https://www.googleapis.com/discovery/v1/apis/webmasters/v3/rest"
]
});
},
gapiList() {
this.initClient()
.then(() => {
// Executes an API request, and returns a Promise.
// The method name `webmasters.sites.list` comes from the API webmasters.
return gapi.client.webmasters.sites.list();
})
.then(
response => {
console.log(response.body);
},
err => {
console.error(err.details);
}
);
},
Here's the code that request the API:
gapi.load("client", this.gapiList);
It does return me good data.
But my final purpose requires me to use a service account.
The initClient function does need a client_id to load correctly. If I'm giving the client_id of the service account it does return me an error.
"Not a valid origin for the client: http://localhost:8080/ has not been whitelisted for client ID xxxxxxxxxxxxx. Please go to https://console.developers.google.com/ and whitelist this origin for your project's client ID.
The error message is telling me to whitelist the localhost (where I am currently working) but I don't find how to whitelist localhost for a service account.
Hope I gave enough informations.
Thank for any reply and help.
As the message tells, you need to allow localhost:8080 as an origin to call the API. To protect you and your users, Google restricts your OAuth 2.0 application to using Authorized Domains. If you have verified the domain with Google, you can use any Top Private Domain as an Authorized Domain.
After you add an Authorized Domain, you can use any of its subdomains or pages, and any other associated country codes. Add your Authorized Domains before you add your redirect or origin URIs, your homepage URL, your terms of service URL, or your privacy policy URL.
To accomplish this, follow these steps:
In the GCP Console, click APIs & Services and then OAuth consent screen. You might have to click Menu Menu first.
In the Application name field, enter G Suite Migrate and click Save.
In the left menu, click Credentials.
Click Create credentials and then OAuth client ID.
Select Web application.
In the Name field, enter a name for the OAuth web client.
In the Authorized JavaScript origins field, enter the URL that you’ll use to access the G Suite Migrate platform (for example,
http://localhost:5131).
Click Create.
Make a note of the client ID shown in the Client ID field. You’ll need it when you set up the G Suite Migrate platform. Tip: You can
also access the client ID from APIs & Serviceand thenCredentials.
Click OK.
Note: It might take some time for the authorization process to
complete.
Extra: If you want a service account to be able to call an API on a users behalf, you will also need to delegate domain-wide authority to the service account. This is explained in detail here.
You can not use service account with Google JavaScript client library.
You should use to Oauth2 authentication. If you have to use service account you need to use server sided language such like node.js or python etc.
I'm using the adal-angular library (but not with Angular) in my SPA to try to acquire an access token that I can use to call the SharePoint APIs (https://<my-tenant>.sharepoint.com/_api/).
I've registered an application in Azure AD and enabled the implicit flow in the manifest, and I'm now running my SPA locally, which is why there's a localhost redirect URI. The code below is being executed on startup:
const context = new AuthenticationContext({
clientId: '<my-client-id>',
redirectUri: 'http://localhost:3000/signin',
popUp: true,
loginResource: 'https://<my-tenant>.sharepoint.com',
callback: () => window.location.reload()
});
const user = context.getCachedUser();
if (!user) {
context.login();
} else {
context.acquireToken('https://<my-tenant>.sharepoint.com', (error, token) => {
console.log(error, token);
});
}
I'm already logged into the SharePoint site, so with this config everything happens automatically and I see a JWT access token logged to the console. However, when I inspect the token, I see that the audience is <my-client-id>. When making a call to https://<my-tenant>.sharepoint.com/_api/v1.0/me using the access token, I then get a 401 response with the following error message:
{"error_description": "Exception of type 'Microsoft.IdentityModel.Tokens.AudienceUriValidationFailedException' was thrown."}
I'm pretty sure this all boils down to me not understanding the OAuth2 flow properly, but... how can I acquire a token that SharePoint can actually be used with SharePoint? Am I thinking about this the wrong way? It kinda defeats the purpose if the token retrieved by my app can only be used to authenticate against my own app.
Getting an access token to SharePoint is well described here:
OneDrive for Business authentication and sign in
You should consider first getting a token to the Discovery Endpoint:
Using an access token received for resource
https://api.office.com/discovery/ you can make a request to the
discovery API to learn which services are available
If the call is successful, the response body contains JSON data with
information about the services available for the user and your app.
{
"#odata.context": "https:\/\/api.office.com\/discovery\/v1.0\/me\/$metadata#allServices",
"value": [
{
"#odata.type": "#Microsoft.DiscoveryServices.ServiceInfo",
"capability": "MyFiles",
"serviceApiVersion": "v2.0",
"serviceEndpointUri": "https:\/\/contoso-my.sharepoint.com\/_api\/v2.0",
"serviceResourceId": "https:\/\/contoso-my.sharepoint.com\/"
}
]
}
There you should get your valid Resource ID... but the issue here may just be that you did not include a forwardslash (/) at the end of the Resource URL in your sample code.