Can't get access token from HttpContext for AAD with OpenIDConnect - azure

I have configured Open ID Connect with Azure AD. I wish to retrieve the access_token from AAD. Currently I am only able to retrieve the id_token. I have configured my Azure Active Directory App registration to include both the access_token and the id_token.
I have configured my Azure Active Directory App registration to include both the access_token and the id_token.
I have also tried retrieving the token from the header without any luck.
Startup.cs
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAD", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = true;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.SaveTokens = true;
});
MyController.cs
if(User.Identity.IsAuthenticated)
{
string accessToken = await HttpContext.GetTokenAsync("access_token");
string idToken = await HttpContext.GetTokenAsync("id_token");
}
appsettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mydomain",
"TenantId": "organizations",
"ClientId": "myclientid",
"ClientSecret": "myclientsecret",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath ": "/signout-callback-oidc"
}

You will need to use CodeIdTokenToken response type, according to the documentation
options.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;

I managed to fix this. To anyone that would encounter this issue, set the response type to Code to get both the id_token and the access_token. This will instruct Open ID Connect to use the authorization code flow.
options.ResponseType = OpenIdConnectResponseType.Code

Related

Microsoft Identity Web : Azure AD Client Credential flow with Certificate Based Authentication

I am connecting to Graph API with Microsoft Identity Web (MSAL) library.
[https://github.com/AzureAD/microsoft-identity-web][1]
For this I am using client credentials flow with certificate based authentication.
My configurations are below
Service Registration
services.AddMicrosoftIdentityWebApiAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
appSettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "mydomain.onmicrosoft.com",
"TenantId": "xxxxxxx",
"ClientId": "yyyyyyyy",
"ClientCertificates": [
{
"SourceType": "Path",
"CertificateDiskPath": "c:\\cert\\my-cert.pfx",
"CertificatePassword": "password"
}
] }
For this I am getting the below error
IDW10104: Both client secret and client certificate cannot be null or
whitespace, and only ONE must be included in the configuration of the
web app when calling a web API. For instance, in the appsettings.json
file.
However I am able to accrue token and connect with Graph API using Microsoft.Identity.Client (Using client credentials-flow with certificate based auth)
private GraphServiceClient GetGraphServiceClient()
{
var token = GetToken();
GraphServiceClient graphServiceClient =
new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
// Add the access token in the Authorization header of the API request.
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
})
);
return graphServiceClient;
}
private string GetToken()
{
var x509Certificate2 =
new X509Certificate2(System.IO.File.ReadAllBytes("MyCert.pfx"), "password");
IConfidentialClientApplication app =
Microsoft.Identity.Client.ConfidentialClientApplicationBuilder.Create("my-client-id")
.WithTenantId("my-tenent-id")
.WithCertificate(x509Certificate2)
.Build();
// With client credentials flows the scopes is ALWAYS of the shape "resource/.default", as the
// application permissions need to be set statically (in the portal or by PowerShell), and then granted by
// a tenant administrator
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result =
app.AcquireTokenForClient(scopes)
.ExecuteAsync().Result;
return result.AccessToken;
}
Am I missing any configuration here?
On workaround
Try with the adding the certificate in the Azure App registration
1) Go to the Azure portal. In the left-hand navigation pane, select the Azure Active Directory service, and then select App registrations.
2) In the resultant screen, select the Select the your application.
3) In the Certificates & secrets tab, go to Certificates section:
4) Select Upload certificate and, in select the browse button on the right to select the your existing certificate.
5) Select Add.
For more details refer this document: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/blob/master/4-WebApp-your-API/4-1-MyOrg/README-use-certificate.md

Azure AD Identity authentication error : IDX10214: Audience validation failed

I have an .net core 5 api that I want to protect using Azure AD, and then call it from a console app. So my case is only service-to-service authentication. I registered both the apps to Azure AD an then gave the permission to the console app on the api using App roles.
I succeeded to retrieve a token for the console app passing the right scope :
static async Task Main(string[] args)
{
var app = ConfidentialClientApplicationBuilder
.Create(<MY_CONSOLE_APP_ID>)
.WithClientSecret(<MY_CONSOLE_APP_CLIENT_SECRET>)
.WithAuthority(new Uri($"https://login.microsoftonline.com/<MY_TENANT_ID>"))
.Build();
var result = await app.AcquireTokenForClient(new String[]
{
"api://<MY_API_APP_ID>/.default"
})
.ExecuteAsync();
}
Then in the Startup class of the API, I added the following code :
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration, "AzureAd");
and in the appsettings.json I defined the required settings:
"AzureAd": {
"ClientId": "<MY_API_APP_ID>",
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<MY_TENANT_ID>"
}
Then I marked a controller as [Authorize] but when I call one of its endpoint I get the following error :
{
"code": 401,
"message": "IDX10214: Audience validation failed. Audiences: '[PII is hidden]'. Did not match: validationParameters.ValidAudience: '[PII is hidden]' or validationParameters.ValidAudiences: '[PII is hidden]'."
}
I also tried to prefix the clientId by api:// but got the same error. I decoded the token to ensure the audience was correct and it seems ok since I have "aud": "api://<MY_API_APP_ID>"
Do you have an idea of what I am missing ?
Whatever code provided looks good.
Make sure the Audience config matches the "aud" claim in the access token.
As that part of code is not provided here,
you may be missing an entry of audience in code configuration under services.AddAuthentication(…
Please check if you have given audience entry in any of these ways .
1
.AddJwtBearer(options =>
{
options.Authority = "";
//options.Audience = Configuration["Audience"];
};
(or)
2
.AddJwtBearer(options =>
{
options.Audience = clientId;
options.Authority = authority;
})
(or)
3)
.AddJwtBearer(options =>
{
options.Authority = "";
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
//here you give required valid audiences
ValidateIssuer = true,
ValidAudiences = new List<string>
{
"AUDIENCE1",
"AUDIENCE2"
}
or valid audiences can be like below in place of AUDIENCE1 or 2.
ValidAudiences = new List<string>
{
Configuration["Authentication:ClientId"]
}
The aud (audience) should match your API's id, and make sure required scopes are present .If these are fine , then check if the token you are trying to validate is ID token or Access token .It differs for API and graph api.

Bearer Token not working in AAD B2C .NETCorre

I have a Web API built using ASP.NET Core. I have a React App that will call this API. The identity is managed using AAD B2C. I am running into an issue where the bearer token generated by the app is not recognized by the API.
I am certain that this has to do with my settings because the token itself has all the claims I need (as decoded by JWT.io). However, when I pass it through the code in .NET Core to allow authorization, the ClaimsIdentity has nothing and contains no user information.
I am setting up the instance using the following lines of code:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(options =>
{
configuration.Bind("AzureAdClient", options);
options.TokenValidationParameters.NameClaimType = "name";
}, options => { configuration.Bind("AzureAdClient", options); });
I also have the following configuration:
"AzureAdClient": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "somename.onmicrosoft.com",
"ClientId": "guid here",
"TenantId": "guid here",
"Audience": "https://somename.onmicrosoft.com/tenants-api",
"SignUpSignInPolicyId": "B2C_1_RwSignIn"
}
Am I doing something wrong here?
I was able to get this figured out using a different strategy. The recommended configuration in most examples still does not work.
Instance: https://yourname.b2clogin.com
Domain: yourname.onmicrosoft.com
SignUpSignInPolicyId: the actual name of
your signup/sign in policy
ClientId: the client Id of the API Client.
var section = configuration.GetSection("AzureAdClient");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = $"{section["Instance"]}/{section["Domain"]}/{section["SignUpSignInPolicyId"]}/v2.0/";
options.Audience = $"{section["ClientId"]}";
});

Azure AD multitenancy configuration with .NET Core

I have two applications registered in Azure AD. One for front-end app and one for API(.NET Core). I need these applications to be multitenant. So I have set Accounts in any organizational directory (Any Azure AD directory - Multitenant) selection in AzureAD for both applications. I am using Azure AD V2.0 endpoints. I am able to get the id_token successfully from front-end app. But when it is passed to API, even though I have set TokenValidationParameters.ValidateIssuer to false, it tries to validate the issuer and returns 401 Unauthorized status. It seems TokenValidationParameters.ValidateIssuer flag is ignored.
I noticed a mismatch when I check the log
INFO Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.? [?] - MESSAGE: AzureADJwtBearer was not authenticated. Failure message: IDX10205: Issuer validation failed. Issuer: 'https://sts.windows.net/9967b0b6-c5d3-*************/'. Did not match: validationParameters.ValidIssuer: 'null' or validationParameters.ValidIssuers: 'https://sts.windows.net/{tenantid}/'.
2020-01-13 14:34:51,884 INFO Microsoft.AspNetCore.Authorization.DefaultAuthorizationService.? [?] - MESSAGE: Authorization failed
Please notice here the Issuer is https://sts.windows.net/9967b0b6-c5d3-*************/
But when I decode the id_token I can see the Issuer is set as "iss":"https://login.microsoftonline.com/9967b0b6-c5d3-*************/v2.0" which I think the correct end point for AzureAD V2.0. Could this be a reason for the above unauthorized return.
Below is the authentication related code in ConfigureServices method in Startup.cs
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
Below is the appSettings.json configuration
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "my_doamin.com",
"TenantId": "common",
"ClientId": "https://my_doamin.com/api-test"
}
The API end point with Authorize attribute
[Authorize(AuthenticationSchemes = "AzureADBearer")]
[Route("getPotalAdminUsers")]
[HttpGet]
public async Task<IActionResult> getPotalAdminUsers()
{
//Code
}
I went through some SO questions regarding this issue. But none helped me.
I appreciate any help on this. Thanks.
I found the solution and the reason behind the issue I faced.
I am posting the answer so that anyone who is facing the same issue can get the issue cleared out.
I was using OpenIdConnect middleware in Startup.cs which was the reason behind the issue. OpenIdConnect middleware is used when we need to sign-ing in the users from our app. But in our context we sign-ing in the users from the front-end app. API does not sign-ing in users! API just needs to validate the access_token.
To validate access_tokens we should use JwtBearer middleware instead of OpenIdConnect middleware.
So replace the
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
With
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters.ValidateIssuer = false;
});
For more information refer the below link
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/How-ASP.NET-Core-uses-Microsoft.IdentityModel-extensions-for-.NET

MVC 5 OWIN External Login with Mobile Services

I am doing external login (Facebook, Twitter, Microsoft) using MVC 5 OWIN Identity 2, which works great, but I need to access a mobile services with this credential, I have read that to this I need a access token, so I get the access token and try to pass it to the mobile services, but always has this error:
Facebook: Error:
The Facebook Graph API access token authorization request failed with HTTP status code 400
Microsoft: Error:
Invalid token format. Expected Envelope.Claims.Signature.
The method that I am trying to use with mobile services is:
await mobileservi.LoginAsync(MobileServiceAuthenticationProvider.[ProviderName], token);
I read on this link:
http://msdn.microsoft.com/en-us/library/dn296411.aspx
So I am using a JObject() to pass the access token
The format of the token that I most pass:
For Microsoft is:
token.Add("authenticationToken", _accessToken);
{"authenticationToken":"<authentication_token>"}
For Facebook is:
token.Add("access_token", _accessToken);
{"access_token":"<access_token>"}
But I do not have the format for Twitter.
Now according to Azure Mobile Services documentation, I most use the azure mobile services URL on my apps for any of this providers, but if I do this, I receive an error of incorrect URL when redirecting to the provider log in page.
I read this post with OAuth:
http://blogs.msdn.com/b/carlosfigueira/archive/2013/06/25/exposing-authenticated-data-from-azure-mobile-services-via-an-asp-net-mvc-application.aspx
It has to be something like this for MVC 5 OWIN Identity 2.
On the Startuo.Auth.cs file, I have this configure to get the access token for each provider:
Microsoft:
var MicrosoftOption = new MicrosoftAccountAuthenticationOptions()
{
ClientId = "0000000048124A22",
ClientSecret = "c-gTye48WE2ozcfN-bFMVlL3y3bVY8g0",
Provider = new MicrosoftAccountAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim(("urn:microsoftaccount:access_token", context.AccessToken, XmlSchemaString, "Microsoft"));
return Task.FromResult(0);
}
}
};
Twitter:
var twitterOption = new TwitterAuthenticationOptions()
{
ConsumerKey = "ConsumerKey",
ConsumerSecret = "ConsumerSecret",
Provider = new TwitterAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim("urn:tokens:twitter:accesstoken", context.AccessToken));
context.Identity.AddClaim(new Claim("urn:tokens:twitter:accesstokensecret", context.AccessTokenSecret));
return Task.FromResult(0);
}
}
};
Facebook:
var facebookOption = new FacebookAuthenticationOptions()
{
AppId = "AppId",
AppSecret = "AppSecret",
Provider = new FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
return Task.FromResult(0);
}
}
};
On the externalLoginCallback, this is how a retrieve the access token
string email = null;
string accessToken = null;
ClaimsIdentity ext = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
switch (login.LoginProvider)
{
case "Facebook":
accessToken = ext.Claims.First(x => x.Type.Contains("access_token")).Value;
break;
case "Twitter":
accessToken = ext.Claims.First(x => x.Type.Contains("accesstoken")).Value;
break;
case "Microsoft":
accessToken = ext.Claims.First(x => x.Type.Contains("access_token")).Value;
break;
}
Later I store this value on a session variable, this value is the one that I use to pass as the access token.
So I have no idea what to do, can anyone please help me?
OK, I found what I was doing wrong, in order to respect the authorization flow, I must have APP ID and APP Secret that I register on my app (Google, Facebook, Microsoft, Twitter), on my mobile service. This is the important part, the register URL in the app must be the URL of the web site, after doing this, everything work fine

Resources