ADAL failed to return token in webAPI. Error:Invalid jwt token using userassersion - azure

A native app created which is calling web api.Two apps has been created in the azure.Here is the code code for getting access token and it worked well,I am getting access token:
UserCredential uc = new UserPasswordCredential(userName, password);
result = authContext.AcquireTokenAsync(todoListResourceId,clientId,
uc).Result;
Now to access new token after the expiry of old one(1 hr) i am using the code:
AuthenticationContext authContext = new AuthenticationContext(authority);
UserAssertion userAssertion = new UserAssertion(oldToken, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
AuthenticationResult result = authContext.AcquireTokenAsync(todoListResourceId, clientId, userAssertion).ConfigureAwait(false).GetAwaiter().GetResult();
But I am getting Error as:"Invalid JWT token. AADSTS50027: Invalid JWT token. Token format not valid".
Checked JWT token :it is correct in format can able to decode using jwt.io.
Note: client Id am using for these two code snippet are the same appId.
I know this is the exact duplication of the question asked by devangi.I cannot able to comment on that question that's why I am asking it again.
Any one can able to help me out?
Or
It will be great if any one can able to help with other ways to get token with out using user password since i need to internally generate new token without user enter password again.

For the scenario when user has authenticated on a native application, and this native application needs to call a web API. Azure AD issues a JWT access token to call the web API. If the web API needs to call another downstream web API, it can use the on-behalf-of flow to delegate the user’s identity and authenticate to the second-tier web API .
Please refer to this document for more details about On-Behalf-Of flow . You can also refer to code sample .
For the scenario when when a daemon application(Your web api) needs to call a web API without user's identity , you should use client credential flow to use its own credentials instead of impersonating a user, to authenticate when calling another web service. Code sample here is for your reference .
Please click here for explanation about above two scenarios. Your code is using Resource Owner Password Credentials Grant ,this flow has multi restrictions such as don't support 2FA and is not recommended .
If i misunderstand your requirement , please feel free to let me know .

Related

Authorization_IdentityNotFound while calling https://graph.microsoft.com/v1.0/organization API

I want, when user login he should get list of tenant, from that list, user decide in which tenant he want to redirect
I want to call https://graph.microsoft.com/v1.0/organization API but when I write code to call it , it will returned error, I have get token using below code, it is worked for users API of Graph, but not working for organization api
B2BGraphClient.AccessToken = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", credential).ConfigureAwait(false);
I have checked it using Postman, when I have pass token generated using https://login.microsoftonline.com/common/oauth2/token this api, organization api returns correct output, but logically it is not possible in code to pass userid and password to api and get token, below is image of postman call
I want correct way to do this
Your code is using Client_credentials flow but in Postman you are using ROPC flow.
The two flows use different permission type. Client_credentials flow uses Application permission while ROPC flow uses Delegated permission.
So for Client_credentials flow, if the app belongs to a work or school (organization) context then for https://login.microsoftonline.com/common/oauth2/token replace common with a tenantId or domain name. If you don't do this, you will get the Authorization_IdentityNotFound error.
Specify the tenant in the code (modify the sample code based on your needs).
this.clientId = clientId;
this.clientSecret = clientSecret;
this.tenant = tenant;
// The AuthenticationContext is ADAL's primary class, in which you indicate the direcotry to use.
this.authContext = new AuthenticationContext("https://login.microsoftonline.com/" + tenant);
// The ClientCredential is where you pass in your client_id and client_secret, which are provided to Azure AD in order to receive an access_token using the app's identity.
this.credential = new ClientCredential(clientId, clientSecret);
B2BGraphClient.AccessToken = await this.authContext.AcquireTokenAsync("https://graph.microsoft.com/", this.credential).ConfigureAwait(false);
And don't forget to add one of the following Application permissions Organization.Read.All, Directory.Read.All, Organization.ReadWrite.All, Directory.ReadWrite.All in your app registration as per Permissions.
UPDATE:
In fact this endpoint https://graph.microsoft.com/v1.0/organization can't return the tenants which the user is member of.
You have found the correct Azure rest API to list the tenants.
But this API also doesn't support client_credentials flow.
In another word, you cannot use authContext.AcquireTokenAsync to get the token. You should consider AcquireTokenByAuthorizationCodeAsync. And specify the scope as https://management.azure.com/ instead of https://graph.microsoft.com.
Don't forget to add the Azure Rest permission in app registration.

How to get user details from an Azure AD token?

Using this tutorial as a guide: https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-aspnetcore-webapi
In the web api we need to obtain the users details (for audit purposes we want to record the details of the user in the database).
In the web api, is there a way to decode the azure token to obtain the user details? Or we could simply pass the user email from the client browser in each request also be feasible (this is easily accessible using the MSAL library for Angular)
Rather than trying to decode the token to get the current users email, the base controller exposes the current user as detailed here;
https://learn.microsoft.com/en-us/aspnet/core/migration/claimsprincipal-current?view=aspnetcore-2.1
So simply using the following I can obtain the user associated with the current request;
string email = User.FindFirstValue(ClaimTypes.Email);
You could decode token to get some information of the signed-in user such as username and email, try token with https://jwt.io/.
If you want more details, you can use MS Graph. Me means current signed-in user. Try with following Graph SDK or HttpClient sample.
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
// or graphClient.Users[user-object-id].Request().GetAsync(); for a specific user
var user = await graphClient.Me
.Request()
.GetAsync();

Generating AAD Token with UPN

I am trying to generate a token for a user with below code.
string apiResourceId = "11224320-66b9-4132-8953-9aa485f07004";
string clientId = "bc9869a0-2393-4e42-8c52-845071640ea8";
Uri redirectUri = new Uri("https://localhost:44335/");
string authority = string.Format("https://login.windows.net/{0}",
"rudderless.onmicrosoft.com");
var authContext = new AuthenticationContext(authority);
AuthenticationResult authenticationResult;
authenticationResult = await authContext.AcquireTokenAsync(apiResourceId, clientId,
redirectUri, new PlatformParameters(PromptBehavior.Auto, null));
I have been getting an error in AcquireTokenAsync call -
AADSTS70002: The request body must contain the following parameter:
'client_secret or client_assertion'. Trace ID:
a198696d-8377-40eb-8351-527a25183500 Correlation ID:
24d4b47d-67bf-46c0-a6b7-a248c434512e Timestamp: 2017-09-20 23:09:38Z
Why do I need a client_secret or client_assertion if I want to generate a token when a user is authenticated against a AAD? The type of Client I am using is "Web app /API". However when I am trying to use a Native client I get the token generated but API call to apResourceID is generating unauthorized error.
Few Questions I am seeking help on related to the scinario -
Why I need to provide client_secret when I am using user auth flow?
Why AcquireToken succeed when I change the client Type to Native?
Why the token generated through native client gives an Unauthorize error?
Is there a way for admin to consent on behalf of every user in AAD?
Why I need to provide client_secret when I am using user auth flow?
Web Apps and APIs are considered Confidential Clients. See here for a definition of the different Client Types in the OAuth 2 Specification. These kinds of client always need to use their client secret to authenticate, no matter the flow they are following.
Confidential clients are typically issued (or establish) a set of
client credentials used for authenticating with the authorization
server (e.g., password, public/private key pair).
Why AcquireToken succeed when I change the client Type to Native?
Native Client Applications are a subset of Public Clients. These are defined, in the specification as:
Clients incapable of maintaining the confidentiality of their
credentials (e.g., clients executing on the device used by the
resource owner, such as an installed native application or a web
browser-based application), and incapable of secure client
authentication via any other means.
Therefore, they do not have or need a client_secret to authenticate... but this also means they can only authenticate with user context, whereas a confidential client could authenticate without a user present (Client Credential Flow).
Why the token generated through native client gives an Unauthorize
error?
This is hard to answer without knowing more about the error and the call you are making that causes this error. You should provide more information about this scenario.
Is there a way for admin to consent on behalf of every user
in AAD?
Yes. In the new Azure Active Directory V2 Endpoint, we have an "Admin Consent Endpoint".
Using the older V1 endpoint, we have an &prompt=admin_consent query string which you can read about here.

Xamarin forms - authentication with AAD, tried and failed

I've been trying to get a Xamarin Forms app to get a token from AAD.
I've tried countless different ways of doing it and they all have problems, some are bugs, some I just haven't been able to figure out.
This link is one of the simplest examples I've come across, yet it fails because it says there's no client secret.
http://www.cloudidentity.com/blog/2015/07/22/using-adal-3-x-with-xamarin-forms/
When I add the client secret, by modifying the AuthenticationContext like so
AuthenticationContext ac = new AuthenticationContext("https://login.microsoftonline.com/common");
ClientCredential cc = new ClientCredential("------4ba8-4136-ad1c-f36be878af8a", "-----sDdG4/WZCyYU=");
AuthenticationResult result = await ac.AcquireTokenAsync("https://graph.windows.net", cc);
I get a new error saying there is no application matching graph.windows.net in my directory.
Please make sure the type of the azure ad application your register is Native Client Application , that type application are meant to run on a device and aren't trusted to maintain a secret .
The code you provide is using client credential flow to acquire token , that flow uses app's credentials instead of impersonating a user .

Authenticate App Services backend using Microsoft Graph token?

Edit:
I have added the "id_token" but still get an "Unauthorized" response.
Here is my login code:
PublicClientApplication myApp = new PublicClientApplication("My-AppID-From-App-Registration-Portal");
string[] scopes = new string[] { "User.Read" };
AuthenticationResult authenticationResult = await myApp.AcquireTokenAsync(scopes).ConfigureAwait(false);
JObject payload = new JObject();
payload["access_token"] = authenticationResult.AccessToken;
payload["id_token"] = authenticationResult.IdToken;
user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, payload);
Original Post:
Is it possible to authenticate to a App Services backend using the token retrieved from Microsoft Graph?
I have already tried using this token and calling LoginAsync() with AzureActiveDirectory as the provider, this doesn't work.
JObject payload = new JObject();
payload["access_token"] = GraphAuthenticationHelper.TokenForUser;
user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
Is this possible?
UPDATE: In my original answer, I said you cannot do this. But in reality, you can do this but it's a dangerous thing to do since anyone with a valid Microsoft Graph token could theoretically access your APIs. Before I walk you down that path, let me describe the "right" way to access the Microsoft Graph on behalf of your end user.
The right way to do this is to use the on-behalf-of flow in the mobile backend code to exchange the user's ID token for a Microsoft Graph token. The flow looks like the following:
Client initiates a login with AAD using MSAL and sets the resource to the mobile backend (not the Graph). The result should be a set of tokens.
Client uses the mobile SDK to do a login with BOTH the access_token AND the id_token from #1.
Example code:
JObject payload = new JObject();
payload["access_token"] = {access_token.from.msal};
payload["id_token"] = {id_token.from.msal};
var user = await MobileService.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
payload);
The backend code exchanges the user's ID token (from the x-ms-token-aad-id-token request header) for a graph token. This token exchange is known as "on-behalf-of" and is documented here. I think this can be done using ADAL or MSAL libraries, but I wasn't able to find documentation. It's also simple enough that you could implement the HTTP protocol directly without too much trouble.
The backend uses the newly acquired MS Graph token and makes the graph API call.
You can also cache the graph token that you acquire on the backend so that each API call doesn't require more AAD API calls to do token exchange.
I think no ,please refer to document : https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-dotnet-how-to-use-client-library#a-nameauthenticationaauthenticate-users
Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.
The audience of the access token should be the client ID for your mobile app backend . So if resource is https://graph.microsoft.com/(aud claim in access token) , then Client-managed authentication won't work .

Resources