Another attempt to ask a different question surrounding my month long problem:
I'm now trying to initiate the "On-Behalf-Of" flow to get a MS Graph token when users login with a Microsoft Account. As documented here:
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oauth-on-behalf-of
On the client, users log in with a server-flow:
var user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
On the .NET backend, I am trying to retrieve the MS Graph token:
string clientId = "id-shown-in-app-registration-portal";
string clientSecret = "secret-shown-in-app-registration-portal";
IEnumerable<string> msIdTokenOut = null;
Request.Headers.TryGetValues("x-ms-token-microsoftaccount-access-token", out msIdTokenOut);
string msIdToken = msIdTokenOut.FirstOrDefault();
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/common/oauth2/v2.0");
UserAssertion assertion = new UserAssertion(msIdToken);
ClientCredential cred = new ClientCredential(clientId, clientSecret);
AuthenticationResult authResult = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", cred, assertion);
I get the following error:
aadsts50027: invalid jwt token. token format not valid.
I've tried every possible combination, from using server-flow to login, using MSAL for client-flow (which doesn't authenticate against App Services with the retrieved token). This has been driving me crazy for over a month. I can't believe how many hoops I have jumped through to get 2 Microsoft products working together. If anyone can steer me towards a solution I would be beyond grateful.
Here is a workaround, I suggest you could enable the mobile server custom authentication by using MSAL returned access token.
More details, you could refer to below steps:
Firstly, you could create a login page which will use MSAL login with the microsoft account. It will return the access token.
Then you could send the request with the access token to the mobile service backend to ask for authentication.
Notice: The logic in the backend which used to check the access token is right, you need achieve by yourself. You could decode the access jwt token to get the aud value. If this value is as same as the client id that means the user have the permission to access mobile backend data.
Then you could use jwt token to get the user information from graph api. After get the user information, you could set the user information value to claims to generate the auth token(using this method AppServiceLoginHandler.CreateToken[Add Microsoft.Azure.Mobile.Server.Login NuGet package]). By using this token the mobile client user could access the mobile backend.
The access token like this:
More details, you could refer to this article to know how to enable custom auth in mobile backend.
Related
I'm trying to connect to Graph API and get user access token.
My problem is that I don't know how to connect to Graph API with credentials silently (without browser).
I currently use MSLogin() for get access token but it open a browser where you can authorize an AzureAD app to get some access to your account. A library in Java is litteraly what I want in c# https://github.com/Litarvan/OpenAuth
I need something like: MSGraph.ConnectAsync(email, pass).getAccessToken();
Here my current code (Through a browser)
private const string ClientId = "520f6e8e-xxxxxxxxxxxxxxxxxxxx";
private string[] scopes = { "https://graph.microsoft.com/user.read" };
private static AuthenticationResult authResult;
public static IPublicClientApplication PublicClientApp;
private async Task<AuthenticationResult> MSLogin()
{
PublicClientApp = PublicClientApplicationBuilder.Create(ClientId).WithRedirectUri("msal520f6e8e-xxxxxxxxxxxxxxxxxxxxxxxxxxxxx://auth").Build();
authResult = await PublicClientApp.AcquireTokenInteractive(scopes).ExecuteAsync();
return authResult;
}
If you are using Microsoft Graph .NET Client Library you can check documentation with example how to implement username/password authentication flow.
string[] scopes = {"User.Read"};
var usernamePasswordCredential = new UsernamePasswordCredential("username#domain.com", "password", tenantId, clientId);
var graphClient = new GraphServiceClient(usernamePasswordCredential, scopes);
var me = await graphClient.Me.Request().GetAsync();
You can use AcquireTokenByUsernamePassword() for that, see MSDN.
Note however that Microsoft discourages usage of this flow and depending on your AzureAD setup there might be restrictions (i.e. you can aquire tokens only within a certain IP range etc).
Well, you can get the access token silently but not at the first time, First a user must authorize your app by going through Microsoft's Login flow and for your subsequent calls to Microsoft, you can get the access token without the intervention of user.
I would just give a basic idea, without focusing on a specific SDK that you might be using. For which, you can decide which ever method suits your needs.
I assume, you already have your credentials and desired scopes with you, otherwise you need to obtain those.
Formulate a proper URL using the credentials you obtained, plus you need to add an extra scope in the URL which is offline_access. Then you need to redirect the user to Microsoft for the initial authorization.
If the user logs in successfully, Microsoft will redirect the user back to your website with an Authorization Code.
Grab that Authorization Code and exchange it for an Access Token using /oauth2/{version}/token api.
You will receive a response from above call which will contain an Access Token along with a Refresh Token. You need to store the refresh token somewhere for future use.
Now comes the interesting part.
Using the refresh token, you can renew the access token when it expires without user's intervention. You can use oauth2/v2.0/token api with parameters:
client_id={your_client_id}&scope={your_scopes}&refresh_token={refresh_token_obtained}&grant_type=refresh_token&client_secret={your_client_secret}
The resultant response would look something like this:
{
"access_token": "new access token",
"token_type": "Bearer",
"expires_in": 3599,
"scope": "your scopes",
"refresh_token": "refresh token",
}
REF: https://learn.microsoft.com/en-us/graph/auth-v2-user#authorization-request
Is it possible to acquire a jwt token for the current application and not based on another Azure app or keep it through the entire lifecycle of signed in?
In my app I'm authenticating my user to Azure with Owin and OpenIdConnect. Authentication succeeded and I received a token from Azure.
In later methods I need to pass the token as parameter, so now I store it in session variable, but this expires earlier and results in an empty variable.
Now I'm trying to acquire a token from Azure with ADAL.
string userObjectId = ((ClaimsIdentity)User.Identity).Claims.FirstOrDefault(c => c.ToString() == "http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value;
AuthenticationContext authContext = new AuthenticationContext(ConfigurationManager.AppSettings["ida:AADInstance"] + ConfigurationManager.AppSettings["ida:TenantId"], new TokenCache());
ClientCredential credential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientId"], "client_secret");
var result = authContext.AcquireTokenAsync(ConfigurationManager.AppSettings["ida:ClientId"], credential).Result.AccessToken;
This results in an error:
{"error":"invalid_grant","error_description":"AADSTS50105: Application >
'4337e286-7b84-4377-8843-82ea9504606b' is not assigned to a role for the application '4337e286-7b84-4377-8843-82ea9504606b'}
For other apps, it is set up with another app which has permissions for the resource, but I don't want to depend on another app to get the token.
Anyone an idea?
EDIT
I've followed the suggestion of #Jean-MarcPrieur, but on the line
var accounts = await application.GetAccountsAsync();
it returns no accounts which results in an empty result and no accessToken.
Here you are trying to use the client credentials flow (which requires your app to register permissions to call the Web API you want to call - tenant admin permissions).
If you want to access resources in the name of the user, you need to acquire a token for your app to call the API in the name of the signed-in user.
See for instance this sample: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/aspnetcore2-2-signInAndCallGraph which signs-in an user and calls the graph
It's an ASP.NET Core sample using the Azure AD v2.0 endpoint (the latest technos). If you have other constraints, don't hesitate to ask.
I have created a Web API in Azure.
This Web API makes some calls in SharePoint Online. Some of the api calls are on-behalf-of.
This Web API works fine until 01.05.2018 - and it works fine on old app services, which were created before 01.05.2018.
A microsoft staff member said:
As part of our security hardening efforts we do not allow id_token
redemption for any application created after 2018-05-01 00:00:00.
During the log in process of adal, I got the id_token. The id_token has got the same value as the access_token:
When I call the web api, I will send this token as bearer token.
The Web API takes this token (string accessToken) and starts the method 'AcquireTokenAsync':
var clientID = ConfigurationManager.AppSettings["ClientID"];
var clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
var tenant = ConfigurationManager.AppSettings["Tenant"];
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new AuthenticationContext(
"https://login.microsoftonline.com/" + tenant);
var resource = new Uri(sharePointUrl).GetLeftPart(UriPartial.Authority);
var authResult = await authContext.AcquireTokenAsync(resource, appCred,
new UserAssertion(accessToken));
return authResult.AccessToken;
But in the line which calls 'AcquireTokenAsync' I have got the error message:
AADSTS240002: Input id_token cannot be used as 'urn:ietf:params:oauth:grant-type:jwt-bearer' grant
But where is the problem?
The problem is that you use the same application identity in the front-end and back-end, and MS does not allow you to use the Id token (which you use as an access token here because of the former) to get another access token.
A possible solution:
Register another application (the front-end JS app should be a Native app)
It should acquire an access token for your back-end API using either the API's client id or app Id URI as the resource
Then the API can exchange the access token for another access token
If this is a multi-tenant app, the migration is probably not going to be easy.
If it's single-tenant, then all should be possible.
Your front-end app should of course require permission to call your back-end API in Azure AD, and that permission should be granted.
Another solution would be to acquire the other access token in the front-end using ADAL.JS instead of using on-behalf-of in the back-end and attaching that to all API requests in addition to the Id token.
I am having problem understanding how to implement a Facebook login workflow for my web app. I am stuck in the validation of the user token server side.
I am using C# SDK library. I am trying to validate the user token using the following code:
var fb = new FacebookClient();
fb.AppId = "0000000000000";
fb.AppSecret = "000000000000000000000";
dynamic result = fb.Get("debug_token", new { input_token = c_accessToken });
I get the following error when running the Get command.
OAuthException - #100) (#100) You must provide an app access token
or a user access token that is an owner or developer of the app
I changed the appId and appSecret for privacy reasons, but I double checked them and they are valid.
You need to send App Access Token That is the error you are getting. To get an App access token follow this documentation from facebook
https://developers.facebook.com/docs/facebook-login/access-tokens#apptokens
To generate App Access Token you need to make a GET request from your FacebookClient() or using HttpClient()to this API endpoint.
GET /oauth/access_token
?client_id={app-id}
&client_secret={app-secret}
&grant_type=client_credentials
this the documentation link for 'debug_token' endpoint
https://developers.facebook.com/docs/facebook-login/access-tokens/debugging-and-error-handling
I have a web application. In the home page, user will enter the credentials, and system should validate against Azure AD and proceed further.
When I use a native app, and use UserCredentials, it validates the user, but if I use same approach for WebAPI, it throw the exception
The request body must contain the following parameter: 'client_secret
or client_assertion'
When I use the WebAPI using clientCredentials, it generates the accessToken, which do not validate the user credentials. I also tried passing the credentials as part of httpclient headers in the consequent calls, it is working despite the wrong credentials.
string AzureADSTSURL = "https://login.windows.net/{0}/oauth2/token?api-version=1.0";
string GraphPrincipalId = "https://graph.windows.net";
string userid = "userid";
string password = "pass";
string tenantId = "axxx"; // webapi
string clientId = "bxxx";
string clientSecret = "cxxx";
string authString = String.Format(AzureADSTSURL, tenantId);
var context = new AuthenticationContext(authString);
UserCredential userCredentials = new UserCredential(userid, password);
AuthenticationResult authenticationResult = context.AcquireToken(GraphPrincipalId.ToString(), clientId, userCredentials); // this works only if the clientId corresponds to a native app
ClientCredential clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationResult result = context.AcquireToken(GraphPrincipalId, clientCredential);
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(result.AccessToken, Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(userid + ':' + password)));
httpClient.GetAsync("http://localhost:11455/Login.aspx");
Is there a way to validate the credentials without using native app? Graph API is not a right choice for this I believe.
I was trying to do the same thing, and ran into the same error:
The request body must contain the following parameter: 'client_secret or client_assertion'
I banged my head on it for a while, and then hit up AzureSupport on twitter.
Turns out this type of auth is only supported if you set up the Azure AD App as Native Client Application. If you set it up as a Web Application then you get that error because the only way to access a web application in Azure AD is via client ID + secret.
You can have multiple apps on top of a single AD, so you can just set up a second app as native client to authenticate the same users in that directory.
You can certainly use WebAPI. Here's how to set it up:
If you use Azure Web Apps, which supports ASP.NET MVC then you can use the Azure Active Directory authentication mechanism. Here is a blog post describing how to set it up: https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-how-to-configure-active-directory-authentication/
Once you have that, auth will be enabled for your app and you can configure the AAD app in the portal. See this blog post for more details: http://blogs.technet.com/b/ad/archive/2014/12/18/azure-active-directory-now-with-group-claims-and-application-roles.aspx
Here is an example which shows how to read AAD group claims from a web app: https://github.com/Azure-Samples/active-directory-dotnet-webapp-groupclaims
Once you have the tokens, you can then call a Web API, which is shown by this example: https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect
There's a good list of AAD examples here: https://azure.microsoft.com/en-us/documentation/articles/active-directory-authentication-scenarios/
Short answer: No
I would consider this article to be the authoritive answer as to why.
No web sites/confidential clients
This is not an ADAL limitation, but an AAD setting. You can only use those flows from a native client. A confidential client, such as a web site, cannot use direct user credentials.
Direct use of username an password is [...] a bit of a Faustian pact – the price you pay for its directness is in the many limitations it entails and the reduced flexibility that it imposes on any solution relying on it.