Identity Server 4 access token with user info - identityserver4

So I have began practicing and using Identity Server 4, my goal is to have an authentication and authorization server for all the applications within my organization. I got to the point where I can log in correctly to my identity server from a third application and get my access_token and it works nicely.
The second step is to get my userinfo inside my access_token but when I decode it I get this:
{
"nbf": 1505250392,
"exp": 1505253992,
"iss": "http://localhost:5000",
"aud": [
"http://localhost:5000/resources",
"SecretAPIEndpoints"
],
"client_id": "SecretClient",
"sub": "ebf3fcad-6ab3-4bcd-88ce-0c5794ebdffa",
"auth_time": 1505250391,
"idp": "local",
"scope": [
"openid",
"SecretAPIEndpoints"
],
"amr": [
"pwd"
]
}
So if I use this token I can make my endpoints work correctly but I want to get it one step further and get my SPA to show my user first name and last name and also their email and roles.
I haven't found documentation or examples to make this happen, so any bit of help would be greatly appreciated.

If you want to consume identity data in JS-based client app, ask for an id_token in addition to an access token.
https://openid.net/specs/openid-connect-core-1_0.html#ImplicitFlowAuth

Related

Connect information from Azure AD to local Ad

I am building an application where users login via azure (oauth).
Now I want to find the logged in user in the local active directory, but how can I do that?
There is a promising objectId in the azure web admin ui but I can't find anywhere in my local AD.
I can't use the upn neither because the are not the same in azure as on my local ad.
There must be a way because the aad sync can keep track of changes as well.
This is the information the access token provides:
{
"aud": "00000003-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/3173e872-f73b-47d0-81c6-75e3ac726c21/",
"iat": 1637937947,
"nbf": 1637937947,
"exp": 1637943631,
"acct": 0,
"acr": "1",
"acrs": [
"urn:user:registersecurityinfo"
],
"aio": "ASQA2/8TAAAAha246r1Y5HNoFGcBvCtPqcvDnjil+dqoybNz9Gk57kk=",
"amr": [
"pwd"
],
"app_displayname": "myapp",
"appid": "b958b44a-3b5c-14ac-b245-126b7524b35c",
"appidacr": "0",
"family_name": "Lustig",
"given_name": "Linda",
"idtyp": "user",
"ipaddr": "188.31.41.234",
"name": "Linda Lustig",
"oid": "9da1be1d-af4f-4d26-a9e7-e7e7a692471b",
"onprem_sid":
"S-1-5-21-4559353447-1638600113-3447510865-117118",
"platf": "8",
"puid": "1003200160C8DCC6",
"rh": "0.AXkAcuhzMTv30EeBxnXjrHJsIUu0iLlcXaxEs0dkayUks1x5AO0.",
"scp": "openid profile email",
"sub": "zpFbfmWNaR4YWYhWKHNEf3rVHVlavGX8DVQVQpPst_c",
"tenant_region_scope": "EU",
"tid": "3173e872-b76b-27d0-51c6-75e3ac726c21",
"unique_name": "lustig#mycompany.onmicrosoft.com",
"upn": "lustig#mycompany.onmicrosoft.com",
"uti": "nAcNGfeFek6iNmBwoJ9iAQ",
"ver": "1.0",
"xms_st": {
"sub": "3CEB7AUcrDeE6SKT1qde_FXWVeK9ppxZB576-PaIfwY"
},
"xms_tcdt": 1547151622
}
• Whenever the Azure AD Connect runs and starts synchronizing, it runs a check on every object in the Active Directory and tries to match the existing object in Azure AD as well. There are three attributes that are used for this process, viz., userPrincipalName(UPN), proxyAddresses and sourceAnchor. Thus, a match on userPrincipalName and proxyAddresses is known as a soft match and a match on sourceAnchor is known as hard match. Also, please note that for proxyAddresses attribute, the SMTP attribute in AD is considered as primary email address and used.
• Thus, since as you said, UPN isn’t matching, you can try matching the other two attributes with on premises ones for finding the local users. Also, please note that sourceAnchor attribute is created when on premises AD attributes are first synced with Azure AD. After that, they remain the same for those identities synced and don’t change later unless changed externally. So, to locate the users based on sourceAnchor attribute, you can locate the equivalent attribute in Active Directory, i.e., ObjectGUID or ms-DS-ConsistencyGuid in on premises AD related to the identities that you are trying to locate.
Please refer the following link for more detailed information: -
https://learn.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-install-existing-tenant#sync-with-existing-users-in-azure-ad

Managed Identites and AppRoles in Token

I have a Web API "App Registration" called "BackEnd_API" which defines some Application Roles and User Roles.
{
"allowedMemberTypes": [
"Application"
],
"description": "resource.READ allows you read access to all items in the application",
"displayName": "resource.READ",
"id": "9650cfb9-570d-4b79-1337-a01337ed6c29",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "resource.READ"
},
I then have another Client Application "App Registration" called "Client_App" which consumes that API to which i've assigned the AppRoles "resource.READ" using either Azure_CLI or PowerShell.
In the Azure Portal I can see that the Service Principal is assigned the role.
When i use the Client_Credentials Flow the resulting access token DOES contain that Roles claim which i use on the BackEnd to authorize the caller.
Until Here ALL Good.
Now, I want to consume the same Web API "BackEnd_API" from another Consuming Application using Managed Identities. So I've created another "App Service", enabled System Assigned Identity and assigned the AppRoles "resource.READ" using Azure CLI.
In the Azure Portal I can see that the Service Principal is assigned the role.
I can get a Token using the JS Azure SDK.
var withClientSecretCredential = () => {
require("#azure/core-auth");
require('dotenv').config()
const {
ManagedIdentityCredential
} = require("#azure/identity");
const logger = require('#azure/logger');
logger.setLogLevel('info');
// Load the .env file if it exists
const credentials = new ChainedTokenCredential(
new ManagedIdentityCredential("54e5c672-872f-4866-b067-132973cb0c91"),
);
token = credentials.getToken(['api://e22fd9eb-3088-4155-936a-0919681c3eb5/.default']);
return token
But the received token in this case has no 'role' claims, so the API call fails to authorize.
I double checked roles and assignment all looks good; is this supposed to work ?
Token without 'role' claim.
{
"aud": "e22fd9eb-3088-4155-936a-0919681c3eb5",
"iss": "https://login.microsoftonline.com/45591230-6e37-4be7-acfb-4c9e23b261ea/v2.0",
"iat": 1634550153,
"nbf": 1634550153,
"exp": 1634636853,
"aio": "E2ZgYGguYd9fNkv3pOV5Iduv2655AgA=",
"azp": "7dd894ca-6c1b-45ae-b67c-75db99593a14",
"azpacr": "2",
"oid": "54e5c672-872f-4866-b067-132973cb0c91",
"rh": "0.ARAAYH9ZRTdu50us-0yeI7Jh6sqU2H0bbK5Ftnx125lZOhQQAAA.",
"sub": "54e5c672-872f-4866-b067-132973cb0c91",
"tid": "45597f60-6e37-4be7-acfb-4c9e23b261ea",
"uti": "qOLzTFlmw0yuWeFXXT1pAA",
"ver": "2.0"
}
Thanks for helping.
The token you might be getting should be an access token and the roles you are looking for should be in the id token. Did you tried this by enabling "ID Token" in azure portal?

What's the difference between User.Read vs OpenID/Profile/Email Permissions in AzureAD App Registration for an app that will sign in users?

When creating multi-tenant apps that will use Azure AD Authentication to sign users in, various samples on GitHub seem to suggest that the App Registration should include the following permissions clubbed under OpenId.
email View users' email address
offline_access Maintain access to data you have given it access to
openid Sign users in
profile View users' basic profile
(See for example: https://github.com/OfficeDev/microsoft-teams-sample-auth-node Section 12)
While following the samples on the Azure portal itself, the quickstarts create an App registration with just this one permission
User.Read Sign in and read user profile
Is Microsoft's Azure AD User.Read permission a superset of the generic openid's email, openid and profile permissions?
When creating a consent URL in the https://login.microsoftonline.com/common/adminconsent?client_id={client-id} form, only the User.Read seems to appear.
What is the recommended set of permissions that are recommended for a basic app that needs to sign users in?
I just spent quite a while testing this as part of my own learning today, and here's what I found.
tl;dr
The openid scope will get you an id_token, and an access_token that allows you to call the UserInfo endpoint (https://graph.microsoft.com/oidc/userinfo). If you want to call any of the Graph APIs, (other than UserInfo) then you'll want (at least) the User.Read scope.
The Details
I have a test AzureAD tenant in which I created a brand-new client app registration (with a SPA endpoint) and a brand-new user, thus ensuring there was no residual 'consent' anywhere.
I then used the browser to sign-in with a hand-crafted URL to request only the openid scope. With line breaks added and some chars redacted this was:
https://login.microsoftonline.com/a8257b21-...6263/oauth2/v2.0/authorize?
client_id=b3f87624-...e5b&
response_type=code&
redirect_uri=https://localhost&
response_mode=query&
scope=openid&
state=12345&
code_challenge=LrTIpxRwK...
code_challenge_method=S256&
prompt=login
For this I got the below consent prompt:
It's interesting to note that it asked to "View your basic profile" and "Maintain access to data..." when I didn't ask for profile or offline_access scopes.
I extracted the authorization code from the response and sent it to the https://login.microsoftonline.com/{{tenant_id}}/oauth2/v2.0/token endpoint with the necessary fields.
The id_token I got back didn't include claims the profile scope would imply, showing only the below
{
"aud": "b3f87624-...2fb5367e5b",
"iss": "https://login.microsoftonline.com/a8257b21...263/v2.0",
"iat": 1642367777,
"nbf": 1642367777,
"exp": 1642371677,
"rh": "0.AUYAIXs...L7U2fluAAB0.",
"sub": "tcK...WXUvzWqAc",
"tid": "a82...6263",
"uti": "SjLRuw...jlhAA",
"ver": "2.0"
}
However interestingly the access_token it returned is for the Graph audience (00000003-0000-0000-c000-000000000000) did list the scopes as including profile and email, and did include claims related to those, e.g. (with some omitted for clarity)
{
"aud": "00000003-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/a82...6263/",
"app_displayname": "TestApp",
"appid": "b3f87...67e5b",
"family_name": "Bull",
"given_name": "Pit",
"idtyp": "user",
"ipaddr": "67.183.2.129",
"name": "Pit Bull",
"oid": "08a5...673de",
"scp": "openid profile email",
"sub": "6JrD7...phCH7Y",
"tid": "a825...36263",
"unique_name": "pit#example.dev",
"upn": "pit#example.dev",
"ver": "1.0"
}
(Note: Obviously you shouldn't look in tokens that aren't for your audience, but it's all there in clear text, so?)
I did also get a refresh token back, which I saved for later.
With that access token I could call the UserInfo endpoint and also see the *name claims, which would indicate profile scope (I forget to set the email on my test user, else I'm guessing that would have shown too).
From https://graph.microsoft.com/oidc/userinfo
{
"sub": "tcK6D...UvzWqAc",
"name": "Pit Bull",
"family_name": "Bull",
"given_name": "Pit",
"picture": "https://graph.microsoft.com/v1.0/me/photo/$value"
}
However, with that same access token, if I tried to call any Graph APIs such as https://graph.microsoft.com/v1.0/me or https://graph.microsoft.com/v1.0/organization I was met with the following response.
{
"error": {
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
"innerError": {
"date": "2022-01-16T21:52:18",
"request-id": "e4f58...1611",
"client-request-id": "e4f5...a1611"
}
}
}
By running through the same flow again but this time with the scopes openid profile email, the only difference I could ascertain was that the id_token now included the below claims (and again, probably would have had the email address if I'd configured it on the account)
"name": "Pit Bull",
"oid": "08a5...73de",
"preferred_username": "pit#example.dev",
The access token looked the same, and the UserInfo result was the same (which makes sense if the access_token to auth to it looked the same). I'd also note I didn't get prompted for any additional consent, which would imply profile and email were implicitly added to my initial openid-only scope request.
At this point I took a break to feed the kids, and when I came back a couple hours later decided to try out the refresh token I received (without requesting offline_access scope). It worked fine and I got refreshed tokens, so this seems to be implicitly added also when just requesting openid.
For my next test I sent the same hand-crafted request to authenticate but included the User.Read scope. As expected, I got prompted for the additional consent:
It's interesting to note how it mentions "..and read basic company information". This aligns with the docs at https://learn.microsoft.com/en-us/graph/permissions-reference which state of User.Read: "With the User.Read permission, an app can also read the basic company information of the signed-in user for a work or school account through the organization resource.".
All the tokens I got back from this looked similar to before, but obviously with the addition of the User.Read scp in the access token. Now making requests to certain Graph APIs was met with success. For example:
https://graph.microsoft.com/v1.0/me or https://graph.microsoft.com/v1.0/users/pit#example.dev
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Pit Bull",
"givenName": "Pit",
"jobTitle": null,
"mail": null,
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": "Bull",
"userPrincipalName": "pit#example.dev",
"id": "08a53...b673de"
}
https://graph.microsoft.com/v1.0/me/directReports (I didn't have any direct reports configured, but it was a successful query rather than access denied)
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"value": []
}
And https://graph.microsoft.com/v1.0/organization (trimmed for readability - and again, I'd not set up org details for this account, hence the nulls)
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#organization",
"value": [
{
"id": "a8257...736263",
"businessPhones": [],
"city": null,
"country": null,
"postalCode": null,
"preferredLanguage": null,
"state": null,
"tenantType": "AAD",
"directorySizeQuota": {
"used": 39,
"total": 300000
},
"verifiedDomains": [
{
"capabilities": "Email, OfficeCommunicationsOnline",
"isDefault": false,
"isInitial": true,
"name": "example.onmicrosoft.com",
"type": "Managed"
},
{
"capabilities": "None",
"isDefault": true,
"isInitial": false,
"name": "example.dev",
"type": "Managed"
}
]
}
]
}
So there you have it. Add User.Read if you want to query anything from the Graph APIs, else just use openid (and optionally profile email) if you are happy just signing users in and using the id_token for your needs.
As a footnote to this I'd also recommend reading the couple paragraphs at https://learn.microsoft.com/en-us/graph/permissions-reference#remarks-15 . I was only testing with a v2 endpoint, but it does look like User.Read used to be required just to sign-in with the v1 endpoint, so it being included by default may be a residual effect of that. As it states of the V1 endpoint: "To successfully return an ID token, you must also make sure that the User.Read permission is configured when you register your app."
Hence the question if user.read subsumes open_id, email and profile
perms?
No, user.read does not contain them, they are independent permissions.
I have set these required perms but in the consent popup shown to the
Azure AD admin, email and profile and openid permissions do not show
up; only offlne_access and user.read shows.
email offline_access openid profile is usually the permissions of the OIDC protocol. There are some differences between oauth2.0 and openid connect. If you only need to log in as a user, you only need to use openid connect, and it will only return you the id token of the logged in user. Regarding why the email openid profile is not displayed on the admin consent page, I think this is a problem that is still being fixed, but I don’t think you need to worry about these because these permissions themselves are permissions that do not require the administrator’s consent. When you add them in API permissions, you can use them directly.
At this time, the offline_access ("Maintain access to data you have given it access to") and user.read ("Sign you in and read your profile") permissions are automatically included in the initial consent to an application. These permissions are generally required for proper app functionality - offline_access gives the app access to refresh tokens, critical for native and web apps, while user.read gives access to the sub claim, allowing the client or app to correctly identify the user over time and access rudimentary user information. please refer to this document

Outlook Add-In SSO access token missing email claim

I am creating an outlook add-in and am trying to use the SSO access token as authorisation for my backend.
I have the flow working. I trigger my Add-In, get the access token and use the access token to "login" to my backend.
However, the issue I have is that I am not getting the email claim in the token despite a) adding it to the API permissions in the Azure App Registration, b) adding to the WebApplicationInfo. Should I be receiving it?
Azure App Registration Permissions
Web Application Info
<WebApplicationInfo>
<Id>xxx</Id>
<Resource>api://localhost:3000/xxx</Resource>
<Scopes>
<Scope>openid</Scope>
<Scope>User.Read</Scope>
<Scope>profile</Scope>
<Scope>email</Scope>
</Scopes>
</WebApplicationInfo>
Code to receive token
OfficeRuntime.auth.getAccessToken( { allowSignInPrompt: true })
Token received
{
"aud": "api://localhost:3000/xxx",
"iss": "https://sts.windows.net/my_tenant_id/",
"iat": 1605992211,
"nbf": 1605992211,
"exp": 1605999711,
"acr": "1",
"aio": "E2RgYDDdzii4P/xP/cPTbg4lPk7dJ6c8aQnapLU19aiV+Zy400sA",
"amr": [
"pwd"
],
"appid": "xxx",
"appidacr": "0",
"family_name": "Adams",
"given_name": "Iain",
"ipaddr": "51.111.111.111",
"name": "Iain Adams",
"oid": "my_oid",
"pwd_exp": "157262",
"pwd_url": "https://portal.microsoftonline.com/ChangePassword.aspx",
"rh": "0.AAAAmj9NLsNP80SAITLYeWEJg9YOWdOzUgJBrv-q0ikqsBxHANs.",
"scp": "access_as_user",
"sub": "my_sub",
"tid": "my_tenant",
"unique_name": "iain#abc.com",
"upn": "iain#abc.com",
"uti": "1KNbNttkDUCrKXblaK5BAA",
"ver": "1.0"
}
Whilst I know the email address is being returned in the upn and unique_name claims, AND, I know that oid should be used as the unique identifier for this user, however, I need to lookup based on email address (if it exists).
You need to customize the access token configuration and then add the email as an optional claim. Go to azure portal>App registrations>your app>Token configuration.
Parse the token:
You need to pay attention to: For managed users (the users inside the tenant), it must be requested through this optional claim or, on v2.0 only, with the OpenID scope.
See: Document.
The "Email" value is part of optional claims. It's included by default if the user is a guest in the tenant. For managed users (it means, the users inside the tenant), it must be requested through this optional claim or, on v2.0 only, with the OpenID scope. For managed users, the email address must be set in the Office admin portal (https://portal.office.com/adminportal/home#/users).

Call Microsoft Graph API Using Azure AD 1.0 Endpoint Client Credentials Flow

Is it possible to access the Microsoft Graph API using an access token obtained through the Azure Active Directory 1.0 Endpoint with the Client Credentials OAuth 2 flow?
For example:
POST https://login.microsoftonline.com/{mytenant}.onmicrosoft.com/oauth2/token
grant_type=client_credentials,
client_id={app id registered in azure portal},
client_secret={registered app key},
resource=https://graph.microsoft.com
When I use the token returned from this request, I get the following error trying to call https://graph.microsoft.com/v1.0/groups.
Decoded JWT
Header
{
"typ": "JWT",
"alg": "RS256",
"x5t": "HHByKU-0DqAqMZh6ZFPd2VWaOtg",
"kid": "HHByKU-0DqAqMZh6ZFPd2VWaOtg"
}
Payload
{
"aud": "00000002-0000-0000-c000-000000000000",
"iss": "https://sts.windows.net/{tenant id}/",
"iat": 1504804880,
"nbf": 1504804880,
"exp": 1504808780,
"aio": "Y2FgYDiiO8/s3smXRdxLg87zBPRNAwA=",
"appid": "{client id}",
"appidacr": "1",
"idp": "https://sts.windows.net/{tenant id}/",
"oid": "{enterprise app object id}",
"sub": "{enterprise app object id}",
"tenant_region_scope": "NA",
"tid": "{tenant id}",
"uti": "uIzrJNpHcEGXoQ4ZKZgqAA",
"ver": "1.0"
}
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure.",
"innerError": {
"request-id": "3537d28e-a061-4430-aef5-4a75bf791d90",
"date": "2017-09-07T16:38:26"
}
}
}
I've ensured the application has the correct permissions assigned through the portal. Under Required Permissions > Application Permissions, "Read and write all groups" is selected.
Azure Portal Permissions
Is there anything I'm missing or is this not possible?
In your JWT token, the Audience Value (aud) is wrong.
If you are trying to call https://graph.microsoft.com or any of it's APIs, you need a token with the aud claim of https://graph.microsoft.com or 00000003-0000-0000-c000-000000000000.
The token you have is for the AAD Graph API, https://graph.windows.net a.k.a. 00000002-0000-0000-c000-000000000000
While these two resources look similar in both URL and GUID form, they are completely separate identities. You should confirm throughout your code that you are specifying the correct resource value when retrieving your access token. Your small sample above implies that you are doing it correct, but the token shows that you are not.

Resources