Using IdentitServer4 I've create a client for a windows application. To call into another authentication service (ie, AWS STS) I need to setup federation to my ID server and using an identity token.
Is it possible to get an identity token for a client?
The following code give me the access token but the identity token is null.
var disco = await DiscoveryClient.GetAsync(Properties.Settings.Default.IdentityUrl);
if (disco.IsError)
{
return false;
}
var tokenClient = new TokenClient(disco.TokenEndpoint, _executionContext.ClientID, _executionContext.Secret);
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api.v1");
_executionContext.AgentToken = tokenResponse.AccessToken; // OK
_executionContext.IdentityToken = tokenResponse.IdentityToken; // NULL
No, by definition a client cannot request an identity token for itself. Only on behalf of a user. From the docs:
User
A user is a human that is using a registered client to access
resources.
Client
A client is a piece of software that requests tokens from
IdentityServer - either for authenticating a user (requesting an
identity token) or for accessing a resource (requesting an access
token).
The reason that a client can't request an identity token for itself is because it doesn't have (and can't have) a sub claim:
The presence (or absence) of the sub claim lets the API distinguish
between calls on behalf of clients and calls on behalf of users.
Here's an example on how to request an identity token on behalf of a user using the password grant.
Related
I am trying to use AAD delegated permission Bearer tokens for a Visio VSTO addin to create SharePoint Online pages using CSOM. Initially I was able to get this working entering username / password following Modern Authentication with CSOM for Net Standard
However, I would like for the user to select an existing AAD account. When I attempt to use the following code the Bearer token "aud" claim is consistently set to "00000003-0000-0000-c000-000000000000" which is the Graph API. Whilst a ClientContext object is returned I am getting a HTTP 401 Unauthorized error when performing a page lookup.
The code is as follows
//
// Get Client App
//
var ClientApp = (PublicClientApplication)PublicClientApplicationBuilder.Create(<AAD App ID>)
.WithDefaultRedirectUri()
.WithTenantId(<AAD Tenant ID>)
.WithAuthority(AzureCloudInstance.AzurePublic, <AAD Tenant ID>)
.Build();
//
// Prompt for user to select preferred AAD account
// The returned JWT Bearer Token "aud" claim is 00000003-0000-0000-c000-000000000000
//
var Token = ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync()
.GetAwaiter()
.GetResult();
//
// Get client Context
//
var ClientContext = AuthenticationManager.GetAzureADAccessTokenAuthenticatedContext(<SharePoint Site URL>, Token.AccessToken);
//
// Using the Client Context to query the Site results in HTTP 401
//
ClientContext.Load(ClientContext.Web, p => p.Title, t => t.Description);
ClientContext.ExecuteQuery();
Looking at the code for the AuthenticationManager class in the above link I can see that the AAD Bearer request is passing the following resource request parameter to the SharePoint online URL:
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
So it seems that AAD is setting the Bearer token "aud" claim based upon this parameter. However, when I try and add this parameter using 'WithExtraQueryParameters' I am getting the following error: "AADSTS901002: The 'resource' request parameter is not supported"
Ok, I figured out the problem. The scope needs to be prefixed with the resource:
string[] scopes = { "https://<domain>.sharepoint.com/AllSites.Write", "user.read" }
Then retrieve the token
this.Token = await ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync();
There is a C# application under development that is supposed to be a part of a bigger backend application to process some data. This application is supposed to obtain a token from Azure AD B2C and send it to an HTTP-triggered function where it is supposed to be validated by the following code:
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
$"{_authenticationSettings.Authority}/.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever());
var config = await configManager.GetConfigurationAsync();
_validationParameters = new TokenValidationParameters
{
IssuerSigningKeys = config.SigningKeys,
ValidateAudience = true,
// Audience MUST be the app ID aka clientId
ValidAudience = _authenticationSettings.ClientId,
ValidateIssuer = true,
ValidIssuer = config.Issuer,
ValidateLifetime = true
};
var tokenHandler = new JwtSecurityTokenHandler();
var result = tokenHandler.ValidateToken(authHeader.Parameter, _validationParameters, out var jwtToken);
First, we thought that obtaining an access token from Microsoft Graph API using MSAL would help us but the C# code above threw an invalid signature exception which we discovered makes sense due to this GitHub post. Apparently, we need to obtain an id_token instead in the application and send it to the HTTP-triggered function for validation by the code snippet above.
The application cannot obtain the id_token because it's not supposed to launch Azure AD B2C's login UI to have a user sign-in and redirect it through a URL. What is the solution to this problem so that the application would obtain a token without a UI and send that to the http-triggered function for validation?
Obtaining a token for the AAD B2C tenant without UI is possible in two ways and you should probably pick one depending on what exactly you want to achieve:
user token - by using Resource Owner Password Credentials flow - https://learn.microsoft.com/en-us/azure/active-directory-b2c/add-ropc-policy. This flow is deprecated though and mentioned usually in legacy application context
server-side application token - by using Client Cretendial flow - this on the other hand requires using requests specific for AAD but with AAD B2C tenant - https://learn.microsoft.com/en-us/azure/active-directory-b2c/application-types#daemonsserver-side-applications
I'm also not quite sure why should you use id_token for that. If the application needs to authorize the request to the function with the token then it should be an access token regardless of how the token is retrieved (interactive UI or not).
My scenario is that there is only one user for a client and that user will be used to fetch/create the API data. I want to use authorization code flow (or whichever suits?) and silently authenticate that one user and generate id_token and access token. My client don't want the login screen to authenticate (perhaps they already have user authenticated in their app) for now as there is only going to be one user.
How will this be achieved and using what grant_type ? Or it there a way to Auto login user using their username and password?
If you have their username and password you can use the Token Endpoint:
POST /connect/token
client_id=yourclientid& client_secret=yourclientsecret&
grant_type=password&
username=yourusername&password=yourusernamespassword
You can use Identity Model to help you make the token request:
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "yourclientid",
ClientSecret = "yourclientsecret",
UserName = "yourusername",
Password = "yourusernamespassword"
});
I have enabled AAD Authentication for an Azure Function and then tried to consume the Function App (HTTP Trigger) in a web application but getting Unauthorized issue.
I also tried consuming it by creating a function proxy but the issue still persists.
Process Followed:
Created two AD Application (Web App, Azure Functions) and gave the
permission of Azure Functions AD to the Web App AD Created a basic
http trigger function
Enabled Authentication for Azure Functions by providing the details of Azure
Functions
Created a web application and during the access token generation, provided
the Client ID,Secret of web application and Audience URI( App ID) of Azure F
Unctions AD.
ClientCredential clientCredential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientId"], ConfigurationManager.AppSettings["ida:SecretKey"]);
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority);
AuthenticationResult result = await authContext.AcquireTokenAsync(ConfigurationManager.AppSettings["azrfunc:ResourceID"], clientCredential);
string requestUrl = "https://xxxx.azurewebsites.net/api/HttpTriggerCSharp1?code=Gxxxxx==&name=xxxx";
// Make the GET request
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
According to your description, I assumed that you are using Authentication and authorization in Azure App Service for your azure function app.
And as How authentication works in App Service states as follows:
Users who interact with your application through a web browser will have a cookie set so that they can remain authenticated as they browse your application. For other client types, such as mobile, a JSON web token (JWT), which should be presented in the X-ZUMO-AUTH header, will be issued to the client. The Mobile Apps client SDKs will handle this for you. Alternatively, an Azure Active Directory identity token or access token may be directly included in the Authorization header as a bearer token.
Based on your scenario, I created my two aad apps and set the required permission for my web app to access the aad app of my function app as follows:
And enable AAD authentication for my azure function app as follows:
Then getting the access token by using the following code:
var clientCredential = new ClientCredential("{clientId-for-my-web-app}", "{clientSecret-for-my-web-app}");
var authContext = new AuthenticationContext("https://login.windows.net/{tenantId}");
var result = await authContext.AcquireTokenAsync("{clientId-for-my-function-app}", clientCredential);
TEST:
In summary, you could decode your access token by using https://jwt.io/ and check the aud as follows:
Moreover, I noticed that your requestUrl contains the query string code. If you both enable the function level authorization and the user-based authentication, you also need to make sure your function key or master key is correct. Also, you could just set the anonymous authorization level for your azure function and just leverage the user-based authentication.
I've got a single page app that authenticates users in Azure using adal-angular.js/adal.js [client].
The returned token is inserted into the auth header and passed to a web API [server]. This web api generates a new access token for the app using the on-behalf-of workflow (https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof)
This token is then used to call a downstream API [API1].
So the downstream API then repeats this process to get a new token to call another API [API2]. It's at this point that I'm getting the above error.
The aud value in the token passed from [client] to [server] is the application id of the [server] app.
The aud value in the token passed from the [server] to [API1] is the Application URI of the [API1] app.
So far so good.
When I call AcquireTokenAsync in [API1] app, I get the following error:
AADSTS70002: Error validating credentials. AADSTS50013: Assertion audience claim does not match the required value. The audience in the assertion was 'http://application_uri.com/' and the expected audience is 'snip-a1d5-e82e84f4e19e' or one of the Application Uris of this application with App ID 'snip-a1d5-e82e84f4e19e'
The relevant code from [API1]:
public static async Task<string> GetApplicationTokenOnBehalfOfUser(string appId, string appKey)
{
var clientCredential = new ClientCredential(appId, appKey);
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as
System.IdentityModel.Tokens.BootstrapContext;
var userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
var userAccessToken = bootstrapContext.Token;
var userAssertion = new UserAssertion(userAccessToken, _assertionType, userName);
var authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, _aadInstance, _tenant);
var userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var authContext = new AuthenticationContext(authority, new TokenCache());
var result = await authContext.AcquireTokenAsync(_resourceId, clientCredential, userAssertion);
var accessToken = result.AccessToken;
return accessToken;
}
Where:
appId = "snip-a1d5-e82e84f4e19e"
And the "aud" value from the BootstrapContext.Token is:
"aud": "http://application_uri.com/"
If I change the above to use the "aud" value from the token as the appId in the ClientCredential, I get this error instead:
AADSTS65001: The user or administrator has not consented to use the application with ID 'http://application_uri.com/'. Send an interactive authorization request for this user and resource.
Am I doing this right?
Thanks.
AADSTS70002: Error validating credentials. AADSTS50013: Assertion audience claim does not match the required value. The audience in the assertion was 'http://application_uri.com/' and the expected audience is 'snip-a1d5-e82e84f4e19e' or one of the Application Uris of this application with App ID 'snip-a1d5-e82e84f4e19e'
To use the on-behalf-of flow, we need to provide the access token for the API1 and provide the clientId and secrect of API1 to request the access token for the API2.
AADSTS65001: The user or administrator has not consented to use the application with ID 'http://application_uri.com/'. Send an interactive authorization request for this user and resource.
Before that tenant users can use the app, the corresponding service principal must be register to that tenant first by permission grant. Is API2 is not in the tenant of the users sign-in?
If I understand correctly, we need to specify the knownClientApplications in the manifest of API1(http://application_uri.com/') with the client_id of your SPA, and it also require to set the permission of API1 to the SPA. After that, when the users sign-in your SPA, the API1 app will also register to the users' tenant.
More detail about multi-tier applications please refer the document below:
How to sign in any Azure Active Directory (AD) user using the multi-tenant application pattern
Update( append the test result to explain)
To get this working I had to add the following delegated permissions to API1 for AP2.
Azure Permissions