I've implemented Azure AD for a business (well, not quite finished!) with the purpose being that only those in the business can use it (they must be in the business' Active Directory). And there are special targeted roles (scopes) that only some users are assigned. And the REST endpoints are annotated with these scopes (in the #OAuthBearer(scope) that we've defined). If there is no special scope required for a given endpoint it is simply annotated with #OAuthBearer() to say company auth is required.
I've generally got this working, however when requesting auth_tokens for the API, scopes can't be empty. If there is an endpoint with no included scope then I've nothing to request an auth_token on. And its an error to have no scopes in the call to msal.js / acquireTokenSilent()
Because I'm using passport-azure-ad on the endpoint I need an auth_token.
What can I do? I don't want to define a scope just for this purpose. By virtue of being in the companies Active Directory they have access. To define that scope means assigning people this scope. That will never float with the business.
I tried to pass user.read (as I did to peform the login with msal.js / loginPopup(scopes) (though I found for loginPopup that a scope was optional)). But this scope returns a v1 token (most interestingly is that for loginPopup it returns a v2 token!). And then passport-azure-ad which requires V2 tokens throws an error.
I tried just passing the idToken from the login, however this is missing a scope and passport-azure-ad throws an error.
I tried /.default, my clientId and other things discussed but none work.
Has anyone any thoughts of possible solutions for this?
Related
I want to authenticate users with Azure Active Directory (AD) in a mobile app that calls its own REST API and possibly, make it simple.
Looks like the documented way (here or here) to do so is to
register the API app with AD, expose some scope(s) as delegated permissions
register the mobile app, add these scopes as API permissions to this app
do authorization decisions in the API app based on these scopes
Question:
Now, since I feel the front-end and back-end parts of my app should belong into the same "black box" plus there are no fine-granular user roles within the app that would justify usage of multiple scopes or require the user to consent to using them, I'm wondering whether there is a recommended (and secure) way to go with just one app registration instead of two?
What I tried:
When using Okta in a similar scenario, I only had one app (clientId) and the back-end configuration pretty much validated the JWT token issuer, domain and a default audience string (in my understanding). I tried inspecting tokens from AD acquired via the authorization code flow for usual scopes (openid profile) to see what their audience was and if this could be reproduced - this is what I've got:
the well known GUID of Microsoft Graph (for the access token) - this one doesn't feel "correct" to validate, as pretty much any AD user could present an access token for MS Graph and only assigned users should be able to use my app
client ID of the app (for the ID token) - but the docs says these should not be used for authorization, so not sure if it's a great idea to pass them as Bearer tokens to the API
The standard thing in OAuth technologies is to only register a client for your mobile app. Azure has some vendor specific behaviour when it comes to APIs, related to the resource indicators spec. When there is no API registration, your mobile app will receive JWT access tokens with a nonce field in the JWT header. These are intended to be sent to Graph, and will fail validation if you ever try to validate them in your own APIs.
If like me you want to stay close to standards, one option is to add a single logical app registration, to represent your set of APIs. You might design an audience of api.mycompany.com, though Azure will give you a technical value like cb398b43-96e8-48e6-8e8e-b168d5816c0e. You can then expose scopes, and use them in client apps. This is fairly easy to manage. Some info in my blog post from a couple of years back might help clarify this.
Now, since I feel the front-end and back-end parts of my app should belong into the same "black box" plus there are no fine-granular user roles within the app that would justify usage of multiple scopes or require the user to consent to using them, I'm wondering whether there is a recommended (and secure) way to go with just one app registration instead of two?
You can create just one app registration; in fact the newer app registration model was designed to do that.
In the case where an API is used only by the app itself, I would register a single scope, something like MobileApp.Access.
Your API should verify the presence of this scope to prevent unauthorized applications from calling it.
In addition to verifying the scope, you will need to verify user permissions.
And filter the data based on their identity, depending on your use case.
Your question seems to suggest that you might be mixing scopes and user roles.
Scopes are permissions given to an application.
Also called delegated permissions; they allow an app to do some actions on behalf of the signed in user.
The goal is to have an application, running on the user's computer authenticated as either themselves or a service principal, that can perform Azure resource management operations and Key Vault secret (data plane) operations.
So far, the resource operations are working fine with the management.azure.com audience, but the Key Vault data operations naturally fail with an error
Invalid audience. Expected https://vault.azure.net, found: https://management.azure.com
I've pored over the docs but it's not entirely clear to me if it's possible to have a single token for both audiences? Here, e.g., it suggests you can simply request multiple scopes, but other sources seem to say they must be for the same resource?
Several of MSAL's token acquisition methods require a scopes parameter. The scopes parameter is a list of strings that declare the desired permissions and the resources requested.
A single token would vastly simplify the application, similar to how ADAL used to work.
You cannot acquire one token for multiple audiences in AAD. This is not supported for token acquisition and also not by the actual services. This is not a MSAL limitation, ADAL cannot do this either. ADAL might request/store multiple tokens internally. You need to manage multiple tokens in this case.
Everything documented by Venkatesan is correct, just giving it a bit more context as an explanation.
See also https://github.com/MicrosoftDocs/azure-docs/issues/82875
I tried to reproduce in my environment and got below results:
Invalid audience. Expected
https://vault.azure.net , found: https://management.azure.com
The above error refers that audience of your token to call Azure Keyvault REST API you need to call the audience with https://vault.azure.net
I tried with postman to get token in my environment, ensure you have Registered AD application and keyvault with right process.
Before that check your Azure Keyvault policy and secrets to ensure that no Authorized application is chosen, and use a service principle rather than an application to access the secret.
When I tried with scope https://management.azure.com in postman and got access token.
Then I copied the token to call the REST API to set secret.I got similar error.
When I tried with scope https://vault.azure.net I got an output sucessfully.
Then I copied the token and called API & set secret, it will work fine.
When I decoded token i'm getting aud with jwt.ms
Reference:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent
I try to understand if there is a way to ask all 'Scopes' of the user in Azure B2C and not specific ones?
Another question, what happens if we request for scope = x y z but the user has only x, it returns only x?
For example, if an app does not recognize all the user scopes and wants to use the user token to access other resource protected with scope x
Short answer - no. This would be in conflict with OpenID Connect specification. As described in the Authorization request scope is required parameter. With some additional explicitly listed values. The OAuth RFC is even more vague when it comes to defining the scope in the authorization request.
One thing worth noting is that you can, anyway only request scopes belonging to single resources in one request. That is in addition to the OIDC standard scopes. So you can only request scopes for one API (i.e. "https://my.api/api/read") but not for more than one ("https://my.api/api/read https://my.other.api2/api/read").
Bottom-line - no, you have to explicitly call out all the scopes that you require, and there is no way to ask the end-user "give me access to everything you have access to"..
Oh, and something more, when you configure your applications in a B2C tenant, you define the required scopes at the application level. For example:
Now all the users who sign-in to the SmartCollabClients application will be granted access to the 3 scopes defined and publish by the SmartCollabApi application.
I try to understand if there is a way to ask all 'Scopes' of the user in Azure B2C and not specific ones?
No, currently it is not possible. All the scopes you should explicitly request.
Another question, what happens if we request for scope = x y z but the user has only x, it returns only x?
No, if you request of scopes x,y,z (which are already associated for requested clientid), B2C will return same scopes irrespective of user privileges
Following SO post gives you few more details
What use are 'Scopes' in Azure B2C Authentication?
We currently use the following authorize url:
https://login.microsoftonline.com/common/oauth2/authorize?resource=https%3A%2F%2Foutlook.office365.com
We want to also use the Graph API, so I added the following:
https://login.microsoftonline.com/common/oauth2/authorize?resource=https%3A%2F%2Foutlook.office365.com%2F%26https%3A%2F%2Fgraph.microsoft.com
I've tried different delimiters between the two resources, but couldn't get it to work. Each one resource works separately. I hope that more than 1 resource at a time is supported?
I think what you're trying to do here by passing multiple values to resource parameter directly will not work (probably not a supported scenario, but I'll wait till someone from Microsoft confirms or I find Azure AD documentation stating exactly that. In the meanwhile, here's an old blog post that says something like this, but it's a blog talking about SSO and old from 2014 :), so don't want to rely solely on this.)
Below I'm explaining how you can make this scenario work by reusing refresh tokens and without passing both resource ids in same call.
(NOTE: This approach will work for Authorization Code Grant Flow but not for Implicit grant flow like a JavaScript based SPA, because no refresh token is returned in that case)
Once the authorization code is available from authorize endpoint, you go to Azure AD token endpoint requesting token for a single resource (using REST call to endpoint or something like ADAL library AcquireToken method depending on your application requirements)
You get back an access token + refresh token as a response to your call to token endpoint. The access token is valid for resource that was mentioned in first call (say graph.microsoft.com)
Then using refresh token you just got, you make another call to token endpoint (REST or ADAL AcquireTokenSilent so that there isn't a popup to ask for user credentials this second time) and get a token for the second resource by specifying the 2nd resource id in case of this call
The access token you get this time is valid for the 2nd resource.
In fact you can continue doing this and hence the name Multi-resource refresh tokens shows up in some places. Although now all refresh tokens are supposed to be multi-resource or valid to be used for requesting any resource that your application has consent for.
Links that can help you in understanding further and implementation
Call Multiple Services With One Login Prompt Using ADAL
Refresh Tokens for Multiple Resources
This SO Post.. look at comments as well.
This SO Post
I'm trying to understand conceptually and practically how to perform an oauth2 with openID-connect flow in my web-api application, utilising Azure AD.
Importantly, when a request is made to the API I want to know who made the request.
My current understanding is :-
My client would detect that the user isn't logged in and redirect to a sign-in.
The user would provide their credentials, and be redirected back to the client, along with an oauth2 token.
This token would be supplied to web-api endpoints for any requests.
This is where it gets murky for me.
How exactly do I use this token to authorize access to a particular resource, determine who is accessing the resource, and what is the mechanism that does so?
I'm sort of assuming that I would need to reuse the token to make a call to the Azure AD user endpoint - if the token was indeed valid, the AD endpoint would return the users details - thereby providing some means of determining that the token is valid and providing details on the users identity. Authorizing access to a resource could be done through membership of groups in Azure AD.
BUT ...
I can only assume this a solved problem, and have noticed use of OWIN middleware as per this example
https://github.com/AzureADSamples/WebApp-WebAPI-OpenIDConnect-DotNet
But I'm still rather unsure as to what is exactly going on.
The service makes mention of scopes and claims, but I don't understand where these are derived from (I assume from a token supplied by the client, but not sure). The service must be receiving identity information in the call.
Which brings me to two points, for this to be secure -
The token provided in call to the service would need to be secured in transmission (hence the use of HTTPS) - to prevent MITM.
The token would need to be signed some how - I guess by using client secret or something - to prevent information in the token being spoofed.
Can someone help me clear up this muddled mess?
In particular -
How is the identity of the API caller determined - is identity determined from a call in the client or the server?
How to limit access to some endpoints of the API based on a user role?
What do I do to practically achieve this by building on existing middleware and libraries available to me?
Disclaimer: This will not be a comprehensive answer. It is off the top of my head.
OpenID Connect provides an identity layer on top of OAuth. In your case, Active Directory provides the authentication and sends back an access_token. The access token represents a user that AD has authenticated. If your doing OpenID Connect, then AD will also send an id_token, which may contain additional identity information (such as birthday, avatar, and whatever else AD exposes.)
Neither OpenID Connect nor Active Directory have anything to do with the the roles that your app assigns to a user; roles are entirely the bailiwick of your app. You assign user roles just like you normally would; you assign them to the nameid though instead of to an email address or username. Your app no longer has to authenticate the user but it does need to assign roles to the nameid.
How is the identity of the API caller determined - is identity determined from a call in the client or the server?
The identity is embedded in the access_token that AD includes in its response. This token will have a nameid in it that your app can associate with a user and role. The nameid is like an email address, username, or other uniqueID that your app uses to recognize the user.
How to limit access to some endpoints of the API based on a user role?
You choose. When your app receives a request with a particular access_token, that token will be associated with a particular user via its nameid, and you can assign whatever roles and rights to that user. Basically, associate roles with a nameid.
What do I do to practically achieve this by building on existing middleware and libraries available to me?
There is an unfinished demo here, though it doesn't use Active Directory as the provider, instead, it uses an internal provider. For the demo, the username is shaun and password is Testing123!. The source code is here.
Here is the link to the source of another demo, though again, it doesn't use Active Directory as the provider, instead, it uses Twitter.
The nice thing about OAuth and OpenID Connect is that we can use whatever identity provider we want, so you can adapt the demos to use Active Directory.
Apart from question #1 (the identity is verified on the service side) all your question are very open ended and would require a super long answer.
I would recommend reading https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-scenarios/ - it is a good introduction to the flows underlying many of the modern authentication cenarios, including the web API one you are focusing on.
Once you have read that, you will find a complete set of samples in https://azure.microsoft.com/en-us/documentation/articles/active-directory-code-samples/ - in particular, I suggest studying the web API and thru authorization one to find guidance on the 3 questions you listed. HTH!