How do I get more recent userinfo with AddJwtBearer - asp.net-core

We use IdentityServer to handle SSO authentication across our apps.
My application is an Aspnet core 3.0 website that passes the users Token to javascript. The javascript then calls a separate aspnet 2.2 API.
Problem: Logging a user out and back in does not update the ClaimsPrincipal on the API with new claims.
I have confirmed that the Web application has the new claims.
If I login Incognito or clear my cookies the new claim shows up in the API.
I am not sure where the responsibility for getting the claims should be and how to fix it. I assume the claims are part of the encrypted access_token, therefore I assume the Web app is sending a stale access_token to the API. So is the Web App what I need to fix? And what would be the proper fix?
Api Startup Code
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Bearer";
options.DefaultChallengeScheme = "Bearer";
})
.AddJwtBearer(options =>
{
options.Authority = oidcSettings.Authority;
options.Audience = oidcSettings.ApplicationName;
options.RequireHttpsMetadata = true;
});
Web App Startup Code
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext => { cookieContext.CookieOptions.SameSite = SameSiteMode.None; };
options.OnDeleteCookie = cookieContext =>
{
cookieContext.CookieOptions.SameSite = SameSiteMode.None; // this doesn't appear to get called.
};
});
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
}).AddCookie("Cookies", options =>
{
options.SlidingExpiration = false;
options.ExpireTimeSpan = TimeSpan.FromHours(8);
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = oidcSettings.Authority;
options.RequireHttpsMetadata = true;
options.ClientId = oidcSettings.ClientId;
options.ClientSecret = oidcSettings.ClientKey;
options.ResponseType = OpenIdConnectResponseType.Code;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("offline_access");
options.Scope.Add(oidcSettings.ApplicationName);
options.ClaimActions.MapJsonKey("role", "role"); // claims I am looking for are mapped here
options.Events.OnUserInformationReceived = async (context) =>
{
await Task.CompletedTask; // confirmed that after new sign in I can see updated info here.
};
});
TLDR: Javascript from Web app calls Api using access_token. When user logs out and logs back in, the API does not receive updated claims. I am not sure if the issue is the API needs to call out to identity server for user info or the Web App is not signing out properly and needs to send a fresh access_token?

Related

How to use cookie authentication for Ocelot apigatway

I am using IdentitySever which issue id_token. Their is another website which uses cookie authentication with openidconnect. Code is as below
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// Allow sign in via an OpenId Connect provider like OneLogin
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromHours(12);
options.SlidingExpiration = false;
options.Cookie.Name = "TestApp";
})
.AddOpenIdConnect(options =>
{
options.RequireHttpsMetadata = false;
options.SignInScheme = "Cookies";
options.ClientId = "Test";
options.ClientSecret = "client_sceret";
options.Authority = "http://localhost:57744/identity";
options.ResponseType = "id_token";
options.GetClaimsFromUserInfoEndpoint = true;
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = redirectToIdentityProvider
};
}
);
services.Configure<OidcOptions>(Configuration.GetSection("oidc"));
}
I am creating another Apigateway. If user is logged in website than should be able to make api request through gateway. Both website and gateway will be hosted under same sub-domain While making request to apigateway I will pass cookie created by website in header and want to utilize it at gateway to get claims from identityserver.
Is their way to integrate cookie authentication and identity sever with ocelot gateway?

Blazor serverside + Cognito Logout?

I'm kinda new to blazor and I'm having issues with logging users out of the application. I've looked at various documents and tutorials but I haven't found anything mentioning logging out. I've tried calling the Cognito logout endpoint (https://docs.aws.amazon.com/cognito/latest/developerguide/logout-endpoint.html) but my application still sees the user as authenticated. I also tried getting the access_token using the answer in this thread How do I get the access token from a blazor (server-side) web app? but it always returns null for me. The isAuthenticated property always return true regardless of what I do. Anyone got any ideas?
In startup
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ResponseType = "code";
options.SaveTokens = true;
options.RemoteSignOutPath = "/signout";
options.MetadataAddress = Configuration["Authentication:Cognito:MetadataAddress"];
options.ClientId = Configuration["Authentication:Cognito:ClientId"];
options.ClientSecret = Configuration["Authentication:Cognito:ClientSecret"];
});
In LoginDisplay.razor
protected override async Task OnInitializedAsync()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
identity = user.Identity as ClaimsIdentity;
var isAuthenticated = identity.IsAuthenticated;
email = identity!.Claims.FirstOrDefault(c => c.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")?.Value ?? string.Empty;
var userData = await userService.GetUserByEmail(email.ToLower());
userData.Then(u =>
userRole = u.Role
, () =>
userRole = UserRole.Anonymous
);
}
According to Microsoft's document AuthenticationStateProvider service, we should not use AuthenticationStateProvider directly since it does not automatically notified of authentication state data changes.
Use AuthrizedView and CascadingStateProvider components instead.

Identityserve 4 in production env : "IDX10501: Signature validation failed. Unable to match key"

I have a 3 tier .NET core application with:
identityserver 4
an API
a blazer app
On my local/dev computer, everything works fine. But, I have installed everything
into a real server and then I have an issue that appears.
I log into the application, play with, then I wait some time (don't know how much) and then when I try to use the app, the Blazor app crash because of this code :
bool isAuthenticated = await _authenticationVerifier.IsAuthenticatedAsync();
if (isAuthenticated)
User = await _userAppService.GetCurrentUserAsync();
The 'isAuthenticated' is true, in fact my cookies look good, but the Blazor app is no more authorized to connect to the API server.
I got the following error message on the API server:
Bearer was not authenticated. Failure message: IDX10501: Signature validation failed. Unable to match key:
kid: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
Exceptions caught: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
token: '[PII is hidden. For more details, see https://aka.ms/IdentityModel/PII.]'.
What is strange is that it work for some time, then after some time (1/2 days), I got this crash.
I don't know what to check and/or how to debug this issue. I'm looking for a solution since weeks :-(
I join some code:
On the API Server:
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = true;
options.ApiName = "MyAppName";
});
}
On the Blazor (Server side Blazor):
private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(ApplicationConstants.LoginCookieExpirationDelay);
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = configuration["AuthServer:Authority"];
options.RequireHttpsMetadata = true;
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.ClientId = configuration["AuthServer:ClientId"];
options.ClientSecret = configuration["AuthServer:ClientSecret"];
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("role");
options.Scope.Add("email");
options.Scope.Add("phone");
options.Scope.Add("SoCloze");
options.ClaimActions.MapAbpClaimTypes();
});
context.Services.AddSingleton<BlazorServerAuthStateCache>();
context.Services.AddScoped<AuthenticationStateProvider, BlazorServerAuthState>();
context.Services.AddScoped<AuthenticationVerifier>();
}
On the IdentityServer 4 side:
// Identity cookie expiration
context.Services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = ".AspNetCore.Identity.Application";
options.ExpireTimeSpan = TimeSpan.FromDays(ApplicationConstants.LoginCookieExpirationDelay);
});
var clientConfig = context.Services.GetConfiguration().GetSection("IdentityServer:Clients");
context.Services
.AddAuthentication(options =>
{
options.DefaultScheme = IdentityConstants.ApplicationScheme;
options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
options.RequireAuthenticatedSignIn = true;
})
.AddFacebook("Facebook", options =>
{
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
options.AppId = clientConfig["Facebook:ClientId"];
options.AppSecret = clientConfig["Facebook:ClientSecret"];
options.Fields.Add("picture");
})
.AddIdentityCookies();
}

Multiple Azure AD Authentication Identity Server 4

I would like to know if it's possible to setup a .NET Core Application using IdentityServer 4 that can Authenticate to more than one AzureAd configuration.
Currently you can add 1 AzureAD configuration like this:
services.AddAuthentication()
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
But I wanted to be able to Authenticate users from multiple Organisations using AzureAd. So different TenantId...etc
This will have to be done on the fly depending on the organisation chosen in the UI.
How can I accomplish that ?
You can use AddOpenIdConnect middleware :
services.AddAuthentication()
.AddOpenIdConnect("AADTenant1", "AADTenant1", options =>
{
options.ClientId = "<app1>";
options.Authority = "https://login.microsoftonline.com/<tenant1>/";
options.CallbackPath = "/signin-oidc-aadtenant1";
options.SaveTokens = true;
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
})
.AddOpenIdConnect("AADTenant2", "AADTenant2", options =>
{
options.ClientId = "<app2>";
options.Authority = "https://login.microsoftonline.com/<tenant2>/";
options.CallbackPath = "/signin-oidc-aadtenant2";
options.SaveTokens = true;
options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme;
})
And trigger the scheme you want to challenge :
var callbackUrl = Url.Action("ExternalLoginCallback");
var props = new AuthenticationProperties
{
RedirectUri = callbackUrl,
Items =
{
{ "scheme", provider },
{ "returnUrl", returnUrl }
}
};
return Challenge(provider, props);

Swagger UI using swasbuckle is getting me CORS Origin issue on login redirect from Azure

I'm facing a new issue with swashbuckle and login redirection after make a request and my token has been expired.
I really don't know if is a common issue or if this thins can't be done, that's why I make the question.
My request to my api enponit redirects fine but, when I click the Execute button on swagger UI, the request to https://localhost:port/api/v1/Contacts, the response is 302 and then try to redirect me to my login provider with this issue and I cannot redirect to login again.
What I'm missing here????
this is my authentication config:
//Add Auth options
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddOpenIdConnect(options =>
{
options.ClientId = ClientId;
options.Authority = $"{Instance}/{TenantId}";
options.SignedOutRedirectUri = SignedOutRedirectUri;
// Add needed scopes
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("roles");
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
options.SaveTokens = true;
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = context =>
{
context.HandleResponse();
return Task.CompletedTask;
}
};
})
.AddCookie()
.AddAzureAdBearer(options => configuration.Bind("bearer", options));