Azure AD OAuth generates token for audience without permission - .net-core

Despite reading multiple articles and tutorials at Microsoft.com, I am having an issue to understand how to define permissions between APIs using app registrations/OAuth2 in Azure AD. To exemplify, I have set up 2 simple app registrations in Azure AD, one for a back end API (lets say client ID A) and another for a front end or another API (client ID B). Then, I set up a basic .NET Core API with default (template) authentication (Options = tenant, client ID, etc) and the default weather forecast endpoint.
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddControllers();
Right now, I am able to get a token from https://login.microsoftonline.com/<tenant>/oauth2/token with Client ID = B and resource = Client ID A, and when I send this token(with 'aud = A') to the API it accepts it.
Why is the token generated successfully, even though I have not set up any relationship between App Registrations A & B? The API Permissions tab in Azure AD is empty in both registrations - I thought AAD would reject the request stating that App B does not have access to App A. Or am I entirely responsible to validate audience claims via code on my app?

It is possible in Azure AD to acquire an access token through client credentials flow to an application that the client app has no permissions on.
This may be to enable some scenarios where the target API handles the whole authorization, but I am not sure.
I wrote an article some time ago on the need to check permissions always: https://joonasw.net/view/always-check-token-permissions-in-aad-protected-api.
I also wrote a follow-up after Microsoft addressed the cross-tenant ability to get access tokens: https://joonasw.net/view/cross-tenant-token-attacks-now-harder-in-azure-ad.
Because a client can get a valid access token without permission assignments,
authorization is crucial on your API side.
At our company we have a default authorization policy that checks every request that it contains either a valid delegated permission/scope or a valid application permission/app role.
That gives us a baseline that already protects the API from tokens like that.
It is usually not the only authorization applied.
In case delegated permissions are supported, you need to check that the user also has access to the thing they are trying to do.
Delegated/app permissions after all only say what the client app can do.

Related

Social Login with OpenID Connect - How to validate on the rest api?

My current setup is the following:
Single Page Application with Vue
Rest Backend
The SPA has social login functionality. It uses the authorization code flow with PKCE to retrieve:
Access Token / Refresh Token
ID-Token
As it is an SPA, the information is stored in local storage and therefore not 100% secure.
But how do I use this information to actually authenticate against my rest backend? The rest backend actually contains the user data I need after all.
Originally, I thought I could just send my access token or id token to my backend and the backend uses this as proof that I'm the correct user.
Main problems I see:
Make sure the access token / id token is actually from my application (the SPA)
Be protected if the tokens are stolen / Minimize the impact of this
For the first problem, the client_id might help, which is embedded in the id-token. It is kind of public information (because it is an SPA) and there is no client_secret. But the redirect_uri is specific to my SPA. Is this enough protection?
If it is my backend could have a list of allowed client ids and providers and check if the client id of the token is one of them.
The second problem is the lifetime of the token. Access tokens are only valid for a short time period and refresh token should be rotated. So it's kind of okay to store these in local storage. But the id_token is valid for a longer time. What do to about this?
Is this in general the right track? Or is my approach completely wrong?

How do you Integrate user data access control with oauth2.0 API's?

I am trying to figure out how OAuth2.0 (or something else entirely) can be used to handle a situation where a user who is calling a backend api, can only retrieve data relevant to that user.
For example:
Lets say I have a bank application, and the customer account information is located at "bank.com/account/{customerId}". How do I restrict access to this, so that other customers cannot see each-others bank account information? As anyone with an access token could get anyone's account info and Roles can't solve this.
I have come up with a potential solution to this problem using Firebase JWTs which is to access the header of the incoming request and compare the User ID in the body of the token to that of the data being accessed.
My gut tells me I am missing the bigger picture, as this problem must be a common phenomena, and I could not find the answer elsewhere.
My Environment is a Spring Boot backend utilizing the Oauth2.0 resource server pointing to the firebase project. Backend is connected to a Postgres database. Frontend is an Angular Application.
To ensure users can access only their own resources, you must write it in your Spring Boot application. OAuth2 only provides you an access token which you can use to find out who is calling you and what scopes he has granted.
But the security logic is up to you to implement. If you have a userId in URLs, you should check that it matches the userId from access token. If user data is stored in a database, you will probably need to add conditions to your SQL queries such as WHERE user_id = :userId.
You can also use scopes from an access token to grant only partial access to user's resources. It's useful if another application can access user's resources on his behalf. Such as reading person's name and email when logging in somewhere using Google/Facebook/Github.

Calling Microsoft Graph API from AAD Authenticated .NET Core API

I currently have a web application that uses Azure AD to authenticate a user. This generates a JWT token which is then sent to my .NET Core Web API, which authenticates and authorizes successfully. The relevant parts of the JWT token that is sent across looks like this when put into jwt.ms (if you need more on this let me know and I can update this):
"aud": "api://<api-clientid>",
"appid": "<webapp-clientid>",
"hasgroups": "true",
"oid": "<userid">,
"tid": "<tenantid>"
I need to get to the groups that the user is a part of, and as the user is part of more than 6 groups, I need to make a call to the Graph API in order to return all groups for the user. This is documented in the description of the access tokens here, which states I should make a call to https://graph.microsoft.com/v1.0/users/{userID}/getMemberObjects.
I have tried using Postman to create a token to authenticate with the Graph API through the on-behalf-of flow, but I get the below error (removed irrelevant parts):
{
"error": "invalid_grant",
"error_description": "AADSTS65001: The user or administrator has not consented to use the application with ID '<api-clientId>' named '<api-displayName>'. Send an interactive authorization request for this user and resource...",
"error_codes": [
65001
],
"suberror": "consent_required",
...
}
I've tried following the web API that calls web APIs guide which seems to do exactly what I want to, using the Microsoft.Identity.Web. When I have tried this, I run into a similar issue with a MicrosoftIdentityWebChallengeUserException saying that I require more consent from the user. I tried changing the audience of the token to be for https://graph.microsoft.com/ but this then fails the authorisation to my web API, and I cannot have multiple endpoints in the audience of the JWT. I also tried using ITokenAcquisition as described here, but this did not work either, with a similar error to Postman.
Finally, I tried following this sample which also appears to do what I require, where I made sure my web API and my web client were set up in the same way that they do. The only change I had to make was a small change to the manifest of the API to include the client application Id in the "knownClientApplications", but this doesn't seem to have made any difference.
I feel like I'm missing something simple here, where either my web application needs to know that my web API might need permission to the Graph API, or I'm missing more configuration on my Azure AD. Any help would be greatly appreciated, and if more information is needed I'll provide as much as I can!
The error message says that admin has didn't consent the application.
Login to Azure AD as Global Administrator and Grant admin consent API permission via App registrations -> API Permissions -> Add a permission -> My APIs -> A -> Application permissions to fix this issue.To fix this issue.

How do I structure authentication with a social IdP?

My system works as follows;
I have an ASP.Net RESTful API server, which contains a user database.
I also have a Xamarin.Forms application, with a registration and login page.
What I want is to have the ability to authenticate a login using a social IdP, and then, if that user has not been logged in before, register that user in my local database.
I have been reading up on how to implement OAuth 2.0 with OpenID Connect to authenticate my users with a social IdP, however, I cannot seem to wrap my head arround it. From what I've read, I shouldn't use an Access token for authentication, since that i what the ID token is for, however, I have also read that the only intended purpose for an ID token, is the client.
My problem then is, how can I make sure that calls made to my ASP.Net server, has been made by "real person", and how do I determine who makes the call?
Access token will be used determine whether the client application was authorized by a user to access a resource. The concept of ID token comes from OpenID Connect. Main purpose of the ID token is to authenticate the user to the client application (i.e. letting the client application know that the person who authorized the access is a valid person).
To do this, you have to validate the ID token. This can be done using third party libraries such as nimbusds or auth0. You can validate the signature of the token verify the integrity of the token and check the claims included in the token (by comparing them with expected values) to verify the user details. Also, you can add custom claims (any claim that is specific for your application/implementation) to the tokens through your identity provider so that you'll be able to validate those particular claims in order to verify the user.

Replacing Google Sign-In for Websites with Cloud Identity-Aware Proxy

There's an open feature request for Metabase to support IAP. I took a stab at it, and have a Clojure implementation of the steps detailed in Securing your app with signed headers (i.e. verify token header, verify token payload, retrieve user identity).
But this question isn't necessarily specific to Metabase. The general idea is to replace Google Sign-In and only use only IAP signed headers for authentication and user creation in an application on Google App Engine (specifically, GAE flex environment).
The "problem" is that the user identity information from the IAP token looks like: {"email":"alice#example.com","sub":"accounts.google.com:118133858486581853996"}. I also came across Using special URLs, but this returns something like: {"email":"accounts.google.com:USER_EMAIL","sub":"accounts.google.com:118133858486581853996"}.
With a Google Sign-In token, I can obtain values for given_name and family_name along with email, which means I can fetch-or-create a valid Metabase user. Is there a way to get the first and last name via the JWT sub, (i.e. accounts.google.com:118133858486581853996)?
Hm, if they have a public profile you can pass the number after "accounts.google.com:" to https://developers.google.com/+/web/api/rest/latest/people/get . Unfortunately, you won't be able to authenticate to that API as the user, since IAP doesn't currently provide a way to call let users delegate access to call Google APIs. (You'll have to use a service account to call that API.)
The other solution would be, if IAP provided a way to a) specify additional scopes in its OAuth request to Google, and if it then b) passed additional claims from the OIDC token into the IAP JWT, you'd be able to configure IAP to request the "profile" scope. However, IAP currently only requests the "email" and "openid" scopes, and doesn't have a mechanism for specifying additional scopes.
-- Matthew, Google Cloud IAP engineering

Resources