I am using Auth0 for Google Authentication for my React App. Login is working successfully and I am getting access token using the getTokenSilently of the auth0-spa-js. But this token do not have user email or name.
const { getTokenSilently } = useAuth0();
getTokenSilently().then((t:any) => {
//t is the token
});
This has following claims:
{
"iss": "https://testauth0.auth0.com/",
"sub": "google-oauth2|<id>",
"aud": [
"test1",
"https://testauth0.auth0.com/userinfo"
],
"iat": 1567615944,
"exp": 1567702344,
"azp": "<>",
"scope": "openid profile email"
}
How can I request email and name to be part of the token? Do I need to pass any parameters to getTokenSilently?
I will be using this token to call an API and I need the email address. An alternative I see is to use the id that is part of the "sub" claim but email is much easier.
Thank you for your help.
Update
I am able to get user info in the API using the userinfo endpoint (part of the aud claim). I would love to avoid this extra call.
You should be able to get the id token via auth0.getIdTokenClaims(). This will have the user profile.
Adding 'email' to the scopes would do the trick
AuthorizationTokenRequest(
AUTH0_CLIENT_ID,
AUTH0_REDIRECT_URI,
issuer: 'https://$AUTH0_DOMAIN',
scopes: <String>['openid', 'email', 'profile'],
),
From Google's OpenId Connect documentation (https://developers.google.com/identity/protocols/OpenIDConnect)
Obtaining user profile information
To obtain additional profile information about the user, you can use
the access token (which your application receives during the
authentication flow) and the OpenID Connect standard:
To be OpenID-compliant, you must include the openid profile scope in your authentication request.
If you want the user’s email address to be included, you can optionally request the openid email scope. To specify both profile and
email, you can include the following parameter in your authentication
request URI:
scope=openid%20email%20profile
Add your access token to the authorization header and make an HTTPS GET request to the userinfo endpoint, which you should retrieve from the Discovery document using the key userinfo_endpoint. The response includes information about the user, as described in OpenID Connect Standard Claims. Users may choose to supply or withhold certain fields, so you might not get information for every field to which your scopes request access.
There's no way to avoid this extra call as you name it.
Adding to dan-woda's answer we need to first add the required information in the claims, here in this case to the accesstoken.
This can be done using a rule.
e.g.
function (user, context, callback) {
context.accessToken["http://mynamespace/user_email"] = user.email;
callback(null, user, context);
}
Check out the samples given example of adding to idtoken
Related
I'm building a react frontend application with a spring backend that is secured with azure ad.
I can't get the authentication flow to work.
In azure ad, I have registered 2 applictions:
API: Default configurations and under "Expose an API" I have added a scope with api://xxxx-api/Access.Api and also added the client application. Under "App Roles" I have added the roles "User" and "Admin". I have assignes both roles to myself.
Client: Registered as SPA with redirect to http://localhost:3000 where the react app is running. Did not check the two boxes for the token to enable PKCE. Under "API permissions" I added the "Access.Api" scope from the api app and granted admin consent.
In the react app I'm using #azure/msal-browser and #azure/msal-react.
My authConfig looks like this:
Then I'm just using useMsalAuthentication(InteractionType.Popup); to sign the user in.
All this works as expected and I'm getting a token back. If I parse this token in jwt.io,
I get "iss": "https://sts.windows.net/42xxxxx-xxxxx-xxxxxx-xxxxx/",
"scp": "openid profile User.Read email", "ver": "1.0",.
However, I do not see the scopes or roles for my API app.
I'm then using an Axios request interceptor to provide the bearer token on every API request:
const { instance, accounts } = useMsal();
const account = useAccount(accounts[0]);
axios.interceptors.request.use(async (config) => {
if (!account) {
throw Error('No active account! Verify a user has been signed in.');
}
const response = await instance.acquireTokenSilent({
...loginRequest,
account,
});
config.headers.Authorization = `Bearer ${response.accessToken}`;
return config;
});
The token is successfully added to the header of each request.
My spring application however fails to validate this token.
My spring config:
I could implement the token validation myself if that is an issue here, but how do I fix, that the bearer token does not contain the roles that I need to check if the user has access to specific resources on the api?
I figured it out.
Basically the scopes openid, email and profile should only be used when requesting an ID token. The ID token contains all roles exposed in the client app.
If these scopes are used when requesting an access token, the token will contain no roles or scopes at all. Only use the scope from the api app that is exposed in the client app when requesting an access token, and the roles will show up in the access token.
I'm trying to authenticate with API Management in Azure through OAuth. I've set up that piece fine.
However from the response, the aud(00000003-0000-0000-c000-000000000000) is invalid from the access token.
Any suggestions/ideas to get the accurate aud in access_token.
I tried to reproduce the same in my environment and got the results like below:
I generated the access token with the same aud as you and got the validation error:
I agree with juunas, To authenticate with API Management in Azure through OAuth, make sure to pass the scope while generating the access token.
I created an Azure AD Application, exposed an API and added scope like below:
Added API permissions like below:
To resolve the error, make sure to pass scope as api://AppID/.default.
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:api://ee1782a6-a994-4013-a396-XXXXX/.default
grant_type:client_credentials
A valid access token to access APIM will be generated like below:
To pass the particular scope from react app using MSAL you can make refer the below sample code:
auth: {
authority: "https://login.microsoftonline.com/common",
clientId: "ClientID",
postLogoutRedirectUri: RedirectURI
redirectUri: RedirectURI
validateAuthority: true,
navigateToLoginRequestUrl: true,
},
cache:
{ cacheLocation: 'sessionStorage',
storeAuthStateInCookie: true,
},
},
{
scopes: ['api://clientid/.default']
},
LoginType.Redirect
References:
OAuth 2.0 Authorisation with the Client Credentials Flow on Azure API Management by Paco de la Cruz
Connect React App with Azure AD using react msal by Ray
You have mistaken the values.
TL;DR: ignore "access token", obtain and read "id token" and verify that "aud" field is your client ID.
First you might obtain a single-use access code (likely something like 0.ABC). Optionally you could fetch open id token. "scope" must include "openid"
Then you can fetch actual open id token using the single-use code. "scope" must be "openid" again. Response might include:
access token - which can be anything including random number of characters, string, your full details or an JWT; I believe that Microsoft returns JWT which is meant to the "00000003-0000-0000-c000-000000000000" audience (meaning "only 00000003-0000-0000-c000-000000000000 can use it - ignore if you are NOT the one")
id token - which is an JWT and should contain your application ID (client ID) in the "aud" field
Always check the "aud" as this says who is the token created for. If it is not you - the token is not for you.
I just wanted to know how can we validate the azure ad access token in a backend API in my case i.e. Django rest framework.
Consider that I have a single page app or a native app and a backend API (django rest framework) completely independen of each other. In my case if my single page app/native app wants to access certain data from the backend API, and inorder to access the API, user should be logged in the backend API.
So what my approch is to make use of MSAL library to get the access token from the SPA/native app and then once token is acquired, pass that token to backend API, validate it, get the user info from graph api. If user exists in the DB then login the user and pass the required info. If user info doesn't exist then create the user, login and pass the info from the API.
So my question is when I pass the access token to my backend api, how can I validate that the token that a user/SPA/native app has passed to backend API is valid token or not?
Is it just we need to make an API call to graph API endpoint with accessToken that user/SPA/native passed and if it is able to get the user data with the accessToken then then token is valid or if it fails then the accessToken is invalid.
Is it the general way to validate the token or some better approach is there? Please help
Good day sir, I wanna share some of my ideas here and I know it's not a solution but it's too long for a comment.
I created a SPA before which used msal.js to make users sign in and generate access token to call graph api, you must know here that when you generate the access token you need to set the scope of the target api, for example, you wanna call 'graph.microsoft.com/v1.0/me', you need a token with the scope 'User.Read, User.ReadWrite' and you also need to add delegated api permission to the azure app.
So as the custom api of your own backend program. I created a springboot api which will return 'hello world' if I call 'localhost:8080/hello', if I wanna my api protected by azure ad, I need to add a filter to validate all the request if has a valid access token. So I need to find a jwt library to decode the token in request head and check if it has a token, if the token has expired and whether the token has the correct scope. So here, which scope is the correct scope? It's decided by the api you exposed in azure ad. You can set the scope named like 'AA_Custom_Impression', and then you can add this delegate api permission to the client azure ad app, then you that app to generate an access token with the scope of 'AA_Custom_Impression'. After appending the Bearer token in calling request, it will be filtered by backend code.
I don't know about python, so I can just recommend you this sample, you may try it, it's provided by microsoft.
I've solved the similar issue. I don't found how to directly validate access token, but you can just call graph API on backend with token you've got on client side with MSAL.
Node.js example:
class Microsoft {
get baseUrl() {
return 'https://graph.microsoft.com/v1.0'
}
async getUserProfile(accessToken) {
const response = await got(`${this.baseUrl}/me`, {
headers: {
'x-li-format': 'json',
Authorization: `Bearer ${accessToken}`,
},
json: true,
})
return response.body
}
// `acessToken` - passed from client
async authorize(accessToken) {
try {
const userProfile = await this.getUserProfile(accessToken)
const email = userProfile.userPrincipalName
// Note: not every MS account has email, so additional validation may be required
const user = await db.users.findOne({ email })
if (user) {
// login logic
} else {
// create user logic
}
} catch (error) {
// If request to graph API fails we know that token wrong or not enough permissions. `error` object may be additionally parsed to get relevant error message. See https://learn.microsoft.com/en-us/graph/errors
throw new Error('401 (Unauthorized)')
}
}
}
Yes we can validate the Azure AD Bearer token.
You can fellow up below link,
https://github.com/odwyersoftware/azure-ad-verify-token
https://pypi.org/project/azure-ad-verify-token/
We can use this for both Django and flask.
You can directly install using pip
but I'm not sure in Django. If Django install working failed then try to copy paste the code from GitHub
Validation steps this library makes:
1. Accepts an Azure AD B2C JWT.
Bearer token
2. Extracts `kid` from unverified headers.
kid from **Bearer token**
3. Finds `kid` within Azure JWKS.
KID from list of kid from this link `https://login.microsoftonline.com/{tenantid}/discovery/v2.0/keys`
4. Obtains RSA key from JWK.
5. Calls `jwt.decode` with necessary parameters, which inturn validates:
- Signature
- Expiration
- Audience
- Issuer
- Key
- Algorithm
I have a mobile client (Android), an API (WebAPI .net Core 3.1) and an IdentityServer4.
I login with my client to IdentityServer and get back an Identity token and a Access Token.
I use the access token to access the API... all good so far.
Now in the API, on the first time only, I need to update a table with the users first and last name plus some other identity stuff, but these claims are not available in the API (because this info is not available in the access token)
My question is, how do I go about getting the user claims from my API code?
I could pass the various claims as string parameters to the API call, but the mobile client is unable to see if this update has taken place, so I would have to pass this information every time I call the API, which is very wasteful as it is only required the first time. I also prefer this to be done without exposing this in an endpoint in my API.
Can anyone suggest how I can get the user (profile) claims of the user with in the API?
Update:
Now I have found an endpoint in IdentityServer that could help, called "userinfo_endpoint" but this needs an Access Token. Can I somehow re-use the Access Token that is used to access the API within my API code?
Yes, you can use the same access token to contact the user info endpoint to get additional user information.
If you were to use JavaScript to access it, it could look like:
//Make a AJAX-call to the user endpoint
$.ajax({
url: "#OpenIDSettings.userinfo_endpoint",
type: 'GET',
headers: { "Authorization": 'Bearer ' + params.access_token }
}).done(function (data) {
$("#userinfo").text(JSON.stringify(data, null, 2));
});
You just set the Authorization header.
You can also add user claims to the access token by providing UserClaims in your ApiScopes or ApiResource definitions, like:
new ApiScope()
{
Name = "shop.admin",
DisplayName = "You can administrate the e-shop systems",
Description = "Full admin access to the e-shop system.",
Emphasize = false,
Enabled = true,
Required = false,
UserClaims = new List<string>
{
//Custom user claims that should be provided when requesting access to this API.
//These claims will be added to the access token, not the ID-token!
"seniority",
}
}
I am attempting to convert over from the old Azure AD OpenId Connect to use the new Azure AD v2.0 endpoint as documented here: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oidc
When I attempt to request a token via the v2.0 token endpoint: https://login.microsoftonline.com/common/oauth2/v2.0/token
I get a response that only includes a 'token_id' field, and not a 'token_type', or any other fields. The library I am using to parse the response is nimbus.com library for openid and auth2. The OIDCTokenReponseParser throws an exception because the 'token_type' is missing from the response.
I have looked at the OpenID Connect Protocol specifications, and it says that a request to the token endpoint requires 'token_type', so it seems as though the response from the endpoint is invalid.
Has anyone run into this issue, and if so, how did you deal with it?
UPDATE 3/2/2018
My flow works with the old end point. I redirect the user here:
https://login.microsoftonline.com/common/oauth2/authorize?response_type=code&client_id={id}&redirect_uri={uri}&scope=openid+profile+email&state={state}
The user logs in, and they are redirected to my app, and code is provided via a query parameter.
I turn around and make this request:
https://login.microsoftonline.com/common/oauth2/token?code={code}&grant_type=authorization_code&client_secret={secret}
And I get response that looks like this.
{
"token_type": "Bearer",
"expires_in": "3599",
"ext_expires_in": "0",
"expires_on": "1520018953",
"access_token": "{token}",
"refresh_token": "{token}",
"id_token": "{token}"
}
I try to handle v2.0 version the same way. I redirect the user to:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=code&client_id={id}&redirect_uri={uri}&scope=openid+profile+email&state={state}
And after they sign in, they are redirected back to my app with the 'code' as a query parameter.
I then send this request:
https://login.microsoftonline.com/common/oauth2/v2.0/token?code={code}&grant_type=authorization_code&client_secret={secret}&redirect_uri={uri}&client_id={id}
But this is the response I get:
{
"id_token":"{token}"
}
The scopes you've requested can all be satisfied with the contents of the ID Token only. In your Auth Request, try including a scope that would indicate that you need an access token (e.g. https://graph.microsoft.com/User.Read), and the response will have the expected token_type and access_token.