How to refresh claims from Identity Server 4 AFTER profile update? - asp.net-core

I'm using aspnet core 1.1 and Identity Server 4. I've created a policy in my client Startup.cs that denied all non-verified email accounts to use some sections of the website.
Here's the code of my policy:
//Add policies
services.AddAuthorization(authorizationOptions =>
{
authorizationOptions.AddPolicy(
ApplicationGlobals.Policy_HasValidatedAccount,
policyBuilder =>
{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.RequireClaim(JwtClaimTypes.EmailVerified, "true",
ClaimValueTypes.Boolean);
});
});
The question is: How can I refresh this EmailVerified claim AFTER the user a confirmed his account? The only way I found was to logout / login ...

If the information you are checking against is in the token, then yes the only way to get a new token is a new token request (aka authentication).
If you need something more dynamic, don't use data from a token.
https://leastprivilege.com/2016/12/16/identity-vs-permissions/

Related

Calling Graph API from ASP.Net Core API on behalf of user from SPA

I'm building an API which is called by a SPA (angular).
The user is authenticated using Azure AD and the API uses AzureAdBearer for its authentication.
public void ConfigureServices(IServiceCollection services)
{
[...]
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureActiveDirectory", options));
[...]
}
I need to call Graph API and would like to do it on behalf of the connected user.
I have seen this question: Accessing MS Graph from API on behalf of user currently signed in to separate web client which really looks like the solution I try to implement.
I try to replicate this proposed solution here: https://joonasw.net/view/azure-ad-on-behalf-of-aspnet-core
As I understand it I need to configure the JwtBearer settings, but how can I call the .AddJwtBearer if I already have .AddAzureADBearer.
AddAzureADBearer itself calls the AddJwtBearer (cf. here)
I created a secret in the app registration of my API and used it to implement a IAuthenticationProvider to get the token on behalf of the user, but I get an error AADSTS65001.
I guess this is because I'm missing the AddJwtBearer configuration to save the user's token in cache, but I'm not really sure.
I don't really understand the solution proposed on the question here.
UPDATE
Thanks to Tony Ju's answer I have been able to authenticate. But when I call graph api I get Authorization_RequestDenied Insufficient privileges to complete the operation.
I don't know why but can't manage to configure Fiddler properly to capture the request to Azure AD. I enabled ADAL logs and got this
[Information][False]: 2020-01-09T09:11:12.1104926Z: AdalLoggerBase.cs: ADAL PCL.CoreCLR with assembly version '5.2.4.0', file version '5.2.4.0' and informational version '5.2.4' is running...
[Information][True]: 2020-01-09T09:11:12.1183853Z: AdalLoggerBase.cs: === Token Acquisition started:
Authority: https://login.microsoftonline.com/d6397071-8e3e-45d2-a2d6-36698acf0fea/
Resource: https://graph.microsoft.com
ClientId: [...]
CacheType: Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache (0 items)
Authentication Target: User
[Verbose][False]: 2020-01-09T09:11:12.1395851Z: AdalLoggerBase.cs: Username provided in user assertion - False
[Verbose][False]: 2020-01-09T09:11:12.5277838Z: AdalLoggerBase.cs: Loading from cache.
[Verbose][False]: 2020-01-09T09:11:12.5395969Z: AdalLoggerBase.cs: Looking up cache for a token...
[Information][False]: 2020-01-09T09:11:12.5467711Z: AdalLoggerBase.cs: No matching token was found in the cache
[Information][False]: 2020-01-09T09:11:12.5706903Z: AdalLoggerBase.cs: No matching token was found in the cache
[Verbose][False]: 2020-01-09T09:11:12.5736362Z: AdalLoggerBase.cs: Looking up cache for a token...
[Information][False]: 2020-01-09T09:11:12.5764248Z: AdalLoggerBase.cs: No matching token was found in the cache
[Verbose][False]: 2020-01-09T09:11:12.5804061Z: AdalLoggerBase.cs: Either a token was not found or an exception was thrown.
[Verbose][False]: 2020-01-09T09:11:12.5844932Z: AdalLoggerBase.cs: Cannot invoke the broker directly, may require install ...
[Verbose][False]: 2020-01-09T09:11:12.5891021Z: AdalLoggerBase.cs: Check and AcquireToken using broker
[Verbose][False]: 2020-01-09T09:11:12.5919785Z: AdalLoggerBase.cs: Broker invocation is NOT required
[Verbose][False]: 2020-01-09T09:11:12.9510499Z: AdalLoggerBase.cs: Storing token in the cache...
[Verbose][False]: 2020-01-09T09:11:12.9583491Z: AdalLoggerBase.cs: An item was stored in the cache
[Information][True]: 2020-01-09T09:11:12.9917987Z: AdalLoggerBase.cs: === Token Acquisition finished successfully. An access token was returned: Expiration Time: 09/01/2020 10:10:54 +00:00Access Token Hash: GtaeLmKsVSj82umwxVcgghW3/X/N2hKhaxyb7XBbBU0=
User id: 3b224748-42c5-406d-a0c0-e9c9f5238361
I used this code to get the token:
var userAssertion = new UserAssertion(token, "urn:ietf:params:oauth:grant-type:jwt-bearer", user.upn);
var authContext = new AuthenticationContext(_aadOptions.Authority);
var clientCredential = new ClientCredential(_aadOptions.ClientId, _aadOptions.ClientSecret);
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", clientCredential, userAssertion);
I tried to rewrite it for MSAL instead of ADAL but didn't success.
Should the scope needed for the graph api request should be in the initial user login or do we expand the scope in the on-behalf-of user request token ?
Please check the permissions of your web api permissions. Go to App registrations->find your web api app->permissions.
If you have permissions which need admin consent, you need to grant admin consent for your tenant.
If you don't have any permissions which need admin consent, try to grant user consent by sending a request like the following example
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=cbc32712-****-4532-802d-303998a6e712
&response_type=code
&redirect_uri=http://localhost
&response_mode=query
&scope=https://graph.microsoft.com
&state=12345
I guess this is because I'm missing the AddJwtBearer configuration to save the user's token in cache, but I'm not really sure.
You can simply set breakpoint in IAuthenticationProvider to confirm whether it gives token via string token = await httpContext.GetTokenAsync("access_token"); . I think it should be there , if there is no access token , you can try below ways :
Override the JwtBearerOptions :
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.SaveToken = true;
options.Events = new JwtBearerEvents
{
OnMessageReceived = async (ctx) =>
{
Console.WriteLine(ctx.Token);
},
OnTokenValidated = async (ctx) =>
{
Console.WriteLine("BreakPoint");
},
};
});
Directly use AddJwtBearer extension .
If using Azure AD v1.0 , you can click here for how to config the On-Behalf-Of flow applications include how to configure known client applications . Compare the request your application sent with the document shows . If using Azure AD v2.0 , you can check this document .

Authentification with AWS Coginito built-in web page

I am building a authentification with AWS Cognito built-in web page.
(I am using Cognito User Pool and Identity Pool without any other provider)
The Cognito webpage return me a token (idtoken) and I put it cookie.
When I try to use this token to get credential, with the following code :
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'eu-west-1:XXXXXXXXXXXXXXXXXXXXXX',
Logins: {
'cognito-idp.eu-west-1.amazonaws.com/eu-west-XXXXXX':
CookieHelper.getCookie("session-id")
}
});
AWS.config.credentials.getId(...);
I get this error :
Error: Invalid login token. Missing a required claim: aud
How can I solve this problem?
Did I miss some steps between to get credentials?
May be I miss somthing in Cognito configuration?
I was using the wrong token.
The build-in webpage return 2 token : One for session-token and one for id-token.
With the other token, I can get credential.
Hope that will help some one.

How to revoke JWT Bearer Token in .net core API

I have implemented JWT Bearer token base authentication and authorization. I am using the below code for destroying the JWT token or logout the current user but it's not working.
//var claim = _httpContextAccessor.HttpContext.User.Claims;
// var users = await _userManager.FindByNameAsync(_httpContextAccessor.HttpContext.User.Identity.Name);
// var identity = _httpContextAccessor.HttpContext.User.Identity as ClaimsIdentity;
// foreach (var item in claim)
// {
// identity.RemoveClaim(item);
// }
await _signInManager.SignOutAsync();
It is not possible to cancel/revoke a JWT token during user's logout, it is not straightforward, but there is a workaround to that. You can try following the steps below:
Set a reasonable expiration time on tokens
Delete the stored token from client side upon log out
Have DB of no longer active tokens that still have some time to live
Query provided token against The Blacklist on every authorized request
I am also pasting 2 links below that myself found very helpful:
How to log out when using JWT
Canceling JWT tokens in .NET Core

How to refresh the access token of an Application with the WSO2 API Manager 1.10.0?

I've (finally) managed to get a valid access token for an application using the API with the WSO2 API Manager 1.10.0. However, it is unclear to me how to refresh this token.
I got the working access token by calling https://myapi.mydomain.com/api/am/store/v0.9/applications/generate-keys?applicationId=2b205ee5-5891-4913-96e0-b4952d7d2a4c with the following payload
var payload = {
'grant_type': 'password',
'username': username,
'password': password,
"validityTime": validityTime,
"keyType": "PRODUCTION",
"accessAllowDomains": [ domain ]
}
The result looks like this:
Generating application keys for application with id '2b205ee5-5891-4913-96e0-b4952d7d2a4c'
{ consumerKey: '5k7UDuFTV0UE7mESHerEIm2Nj3ga',
consumerSecret: 'm1hS_SNfHF25l9lP9YjYpf977VUa',
keyState: 'APPROVED',
keyType: 'PRODUCTION',
supportedGrantTypes:
[ 'urn:ietf:params:oauth:grant-type:saml2-bearer',
'iwa:ntlm',
'refresh_token',
'client_credentials',
'password' ],
token:
{ tokenScopes: [ 'am_application_scope', 'default' ],
validityTime: 2678400,
accessToken: 'be8661550ee51b7682902e58a58108f6' } }
So it does seem like I have the grant to refresh my access token. Howver the documentation only mentions how to do this when you have a refresh token, which is missing from the reply. What am I doing wrong?
The API you used to generate tokens is not the API for your job. The API 'applications/generate-keys' is there to generate application related keys (Client secret and client id) . The token generated for
this has a client_credential grant type. thats why you do not get a refresh token. That token is generated using the client id and secret.
I see you have sent the grant type as 'password' in the payload but those parameters are not valid to start with. that api does not accept grant type name. (api is there to generate the application keys. so grant type is not needed)
Actual sample payload (see https://docs.wso2.com/display/AM1100/apidocs/store/#!/operations#ApplicationindividualApi#applicationsGenerateKeysPost)
{
"validityTime": "3600",
"keyType": "PRODUCTION",
"accessAllowDomains": ["ALL"
]
}
to generate the tokens please use the token api (https://docs.wso2.com/display/AM1100/Token+API). Those apis are implemented according to the OAuth2 specs. You will get a refresh token when you use 'password'
grant type using these token apis
You can refresh the token in API Store (developer portal), if you click on the Subscriptions tab, and there either:
Manually click the Re-generate button refresh right now, or
Click the cURL button to generate the command that you can use to refresh programmatically:
Notes:
You can also read more information about Token API in documentation,
For the screenshot, I used hosted version of API Manager - WSO2 API Cloud.

Why can't I get Amazon Cognito to accept a token from Google+?

I'm doing this all within web pages via javascript.
First I send the user to Google so they can grant access:
https://accounts.google.com/o/oauth2/auth?response_type=token&scope=profile&redirect_uri=https://example.com/&client_id=1414xxxxxxxx-{myclientid}.apps.googleusercontent.com
After granting access they are redirected back to my site with something like this:
https://example.com/#access_token={their_token}&token_type=Bearer&expires_in=3600
Then I take that information and provide it to Amazon like so:
// Initialize the Amazon Cognito credentials provider
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'us-east-1:{my-pool-id}',
Logins: {
'accounts.google.com': "{their_token}"
}
});
AWS.config.region = 'us-east-1';
AWS.config.credentials.get(function(err) {
if ( err ) {
console.log(err);
}
else {
console.log("ID: "+identityId:AWS.config.credentials.identityId);
}
});
And there is where I always end up with an error in the console:
"Error: Invalid login token."
The identity pool 'us-east-1:{my-pool-id}' is setup in my Amazon Console with the Authentication provider 'Google+' setup and a Google Client ID matching the one in the google url (ie. 1414xxxxxxxx-{myclientid}.apps.googleusercontent.com).
I've also tried setting AccountId and RoleArn in the params to CognitoIdentityCredentials but it hasn't helped in this case. Am I using the Logins parameter correctly? What am I missing?
If I take out the Logins parameter or set it to an empty object, I do get an identityId which I assume would be associated with an unauthenticated role. How do I get an identity with an authenticated role using Google APIs as the token provider?
You need to use id_token instead of access_token. More details to obtain an id_token can be found in this documentation : OpenIDConnect