I am using oidc-client
in angular. following this Tutorial
import { UserManager, UserManagerSettings, User } from 'oidc-client';
My Client:
export function getClientSettings(): UserManagerSettings {
return {
authority: 'https://localhost:44305/',
client_id: 'angular_spa',
redirect_uri: 'http://localhost:4200/auth-callback',
post_logout_redirect_uri: 'http://localhost:4200/',
response_type: 'id_token token',
scope: 'openid profile api1',
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: false
};
In Identity Server I'm using Assembly Microsoft.AspNetCore.Identity.UI, Version=2.1.3.0
I'm adding default identity like this:
[assembly:
HostingStartup(typeof(WebApp.Areas.Identity.IdentityHostingStartup))]
namespace WebApp.Areas.Identity
{
public class IdentityHostingStartup: IHostingStartup {
public void Configure(IWebHostBuilder builder) {
builder.ConfigureServices((context, services) => {
services.AddDbContext < WebAppContext > (options =>
options.UseSqlite(context.Configuration.GetConnectionString("WebAppContextConnection")));
services.AddDefaultIdentity < WebAppUser > ()
.AddEntityFrameworkStores < WebAppContext > ();
});
}
}
}
WebAppUser
is derived from IdentityUser
Startup.cs:
public class Startup
{
private ILogger<DefaultCorsPolicyService> _logger;
private IHostingEnvironment _env;
public Startup(ILoggerFactory loggerFactory, IHostingEnvironment env)
{
_logger = loggerFactory.CreateLogger<DefaultCorsPolicyService>();
_env = env;
}
private static void SetupIdentityServer(IdentityServerOptions identityServerOptions)
{
identityServerOptions.UserInteraction.LoginUrl = new PathString("/Identity/Account/Login");
// identityServerOptions.Cors.CorsPolicyName = "CorsPolicy";
}
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
{
builder
.WithOrigins("https://localhost:44305")
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
// services.AddMvc();
var cors = new DefaultCorsPolicyService(_logger)
{
AllowAll = true
};
var cert = new X509Certificate2(Path.Combine(_env.ContentRootPath, "mycert.pfx"), "xxxxx");
services.AddIdentityServer(SetupIdentityServer)//SetupIdentityServer
.AddSigningCredential(cert)
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
// .AddTestUsers(TestUsers.Users)
.AddInMemoryIdentityResources(Config.GetIdentityResources());
services.AddSingleton<ICorsPolicyService>(cors);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//loggerFactory.AddConsole();
app.UseDeveloperExceptionPage();
app.Map("/api", api =>
{
api.UseCors(x => x.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
api.UseAuthentication();
api.Run(async context =>
{
var result = await context.AuthenticateAsync("api");
if (!result.Succeeded)
{
context.Response.StatusCode = 401;
return;
}
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(JsonConvert.SerializeObject("API Response!"));
});
});
app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
//Run these PMC commands after this.
//Add - Migration CreateIdentitySchema
//Update - Database
}
}
In identity server 4 i have enabled https
. So the problem is that from my Angular app if i try to use a protected URL
I'm navigated to identity serves login page. Looks like it is authenticating properly against the user that is in database. but then it just refreshes the login page and does not redirects to the callback URL
.
here are some logs that might help
2019 - 03 - 07 01: 19: 30.553 - 06: 00[INF] Starting IdentityServer4 version 2.3 .2 .0 2019 - 03 - 07 01: 19: 30.632 - 06: 00[INF] You are using the in -memory version of the persisted grant store.This will store consent decisions, authorization codes, refresh and reference tokens in memory only.If you are using any of those features in production, you want to switch to a different store implementation. 2019 - 03 - 07 01: 19: 30.643 - 06: 00[INF] Using the default authentication scheme idsrv for IdentityServer 2019 - 03 - 07 01: 19: 30.644 - 06: 00[DBG] Using idsrv as default ASP.NET Core scheme for authentication 2019 - 03 - 07 01: 19: 30.644 - 06: 00[DBG] Using Identity.External as default ASP.NET Core scheme for sign - in 2019 - 03 - 07 01: 19: 30.645 - 06: 00[DBG] Using Identity.External as default ASP.NET Core scheme for sign - out 2019 - 03 - 07 01: 19: 30.645 - 06: 00[DBG] Using idsrv as default ASP.NET Core scheme for challenge 2019 - 03 - 07 01: 19: 30.645 - 06: 00[DBG] Using idsrv as default ASP.NET Core scheme for forbid 2019 - 03 - 07 01: 19: 31.463 - 06: 00[DBG] CORS request made for path: /.well-known/openid - configuration from origin: http: //localhost:4200 2019 - 03 - 07 01: 19: 31.468 - 06: 00[DBG] AllowAll true, so origin: http: //localhost:4200 is allowed 2019 - 03 - 07 01: 19: 31.468 - 06: 00[DBG] CorsPolicyService allowed origin: http: //localhost:4200 2019 - 03 - 07 01: 19: 31.482 - 06: 00[DBG] Login Url: /Identity/Account / Login 2019 - 03 - 07 01: 19: 31.482 - 06: 00[DBG] Login Return Url Parameter: ReturnUrl 2019 - 03 - 07 01: 19: 31.482 - 06: 00[DBG] Logout Url: /Account/Logout 2019 - 03 - 07 01: 19: 31.482 - 06: 00[DBG] ConsentUrl Url: /consent 2019 - 03 - 07 01: 19: 31.482 - 06: 00[DBG] Consent Return Url Parameter: returnUrl 2019 - 03 - 07 01: 19: 31.482 - 06: 00[DBG] Error Url: /home/error 2019 - 03 - 07 01: 19: 31.482 - 06: 00[DBG] Error Id Parameter: errorId 2019 - 03 - 07 01: 19: 31.497 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 31.550 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 31.553 - 06: 00[DBG] Request path / .well - known / openid - configuration matched to endpoint type Discovery 2019 - 03 - 07 01: 19: 31.569 - 06: 00[DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint 2019 - 03 - 07 01: 19: 31.569 - 06: 00[INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for / .well - known / openid - configuration 2019 - 03 - 07 01: 19: 31.576 - 06: 00[DBG] Start discovery request 2019 - 03 - 07 01: 19: 31.885 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 31.885 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 31.885 - 06: 00[DBG] Request path / connect / authorize matched to endpoint type Authorize 2019 - 03 - 07 01: 19: 31.893 - 06: 00[DBG] Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint 2019 - 03 - 07 01: 19: 31.893 - 06: 00[INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for / connect / authorize 2019 - 03 - 07 01: 19: 31.904 - 06: 00[DBG] Start authorize request 2019 - 03 - 07 01: 19: 31.919 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 31.935 - 06: 00[DBG] No user present in authorize request 2019 - 03 - 07 01: 19: 31.945 - 06: 00[DBG] Start authorize request protocol validation 2019 - 03 - 07 01: 19: 31.983 - 06: 00[DBG] client configuration validation for client angular_spa succeeded. 2019 - 03 - 07 01: 19: 32.069 - 06: 00[DBG] Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator 2019 - 03 - 07 01: 19: 32.099 - 06: 00[INF] ValidatedAuthorizeRequest { "ClientId": "angular_spa", "ClientName": "Angular 4 Client", "RedirectUri": "http://localhost:4200/auth-callback", "AllowedRedirectUris": ["http://localhost:4200/auth-callback", "http://localhost:4200/silent-refresh.html"], "SubjectId": "anonymous", "ResponseType": "id_token token", "ResponseMode": "fragment", "GrantType": "implicit", "RequestedScopes": "openid profile api1", "State": "cd6df66e397546d3aab62533de28a2d2", "UiLocales": null, "Nonce": "8b3af6331d784e9a9cad076555f16174", "AuthenticationContextReferenceClasses": null, "DisplayMode": null, "PromptMode": null, "MaxAge": null, "LoginHint": null, "SessionId": null, "Raw": { "client_id": "angular_spa", "redirect_uri": "http://localhost:4200/auth-callback", "response_type": "id_token token", "scope": "openid profile api1", "state": "cd6df66e397546d3aab62533de28a2d2", "nonce": "8b3af6331d784e9a9cad076555f16174" }, "$type": "AuthorizeRequestValidationLog" } 2019 - 03 - 07 01: 19: 32.126 - 06: 00[INF] Showing login: User is not authenticated 2019 - 03 - 07 01: 19: 32.154 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 32.155 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 32.628 - 06: 00[INF] AuthenticationScheme: Identity.External signed out. 2019 - 03 - 07 01: 19: 40.844 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 40.844 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 41.517 - 06: 00[INF] AuthenticationScheme: Identity.Application signed in . 2019 - 03 - 07 01: 19: 41.518 - 06: 00[INF] User logged in . 2019 - 03 - 07 01: 19: 41.528 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 41.528 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 41.528 - 06: 00[DBG] Request path / connect / authorize / callback matched to endpoint type Authorize 2019 - 03 - 07 01: 19: 41.529 - 06: 00[DBG] Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint 2019 - 03 - 07 01: 19: 41.529 - 06: 00[INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeCallbackEndpoint for / connect / authorize / callback 2019 - 03 - 07 01: 19: 41.535 - 06: 00[DBG] Start authorize callback request 2019 - 03 - 07 01: 19: 41.536 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 41.541 - 06: 00[DBG] No user present in authorize request 2019 - 03 - 07 01: 19: 41.541 - 06: 00[DBG] Start authorize request protocol validation 2019 - 03 - 07 01: 19: 41.541 - 06: 00[DBG] client configuration validation for client angular_spa succeeded. 2019 - 03 - 07 01: 19: 41.541 - 06: 00[DBG] Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator 2019 - 03 - 07 01: 19: 41.541 - 06: 00[INF] ValidatedAuthorizeRequest { "ClientId": "angular_spa", "ClientName": "Angular 4 Client", "RedirectUri": "http://localhost:4200/auth-callback", "AllowedRedirectUris": ["http://localhost:4200/auth-callback", "http://localhost:4200/silent-refresh.html"], "SubjectId": "anonymous", "ResponseType": "id_token token", "ResponseMode": "fragment", "GrantType": "implicit", "RequestedScopes": "openid profile api1", "State": "cd6df66e397546d3aab62533de28a2d2", "UiLocales": null, "Nonce": "8b3af6331d784e9a9cad076555f16174", "AuthenticationContextReferenceClasses": null, "DisplayMode": null, "PromptMode": null, "MaxAge": null, "LoginHint": null, "SessionId": null, "Raw": { "client_id": "angular_spa", "redirect_uri": "http://localhost:4200/auth-callback", "response_type": "id_token token", "scope": "openid profile api1", "state": "cd6df66e397546d3aab62533de28a2d2", "nonce": "8b3af6331d784e9a9cad076555f16174" }, "$type": "AuthorizeRequestValidationLog" } 2019 - 03 - 07 01: 19: 41.541 - 06: 00[INF] Showing login: User is not authenticated 2019 - 03 - 07 01: 19: 41.552 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 41.553 - 06: 00[INF] idsrv was not authenticated.Failure message: Unprotect ticket failed 2019 - 03 - 07 01: 19: 41.553 - 06: 00[INF] AuthenticationScheme: Identity.External signed out.
sorry i tried to format the logs properly but didn't worked.
UPDATE
my server side config looks like this
public class Config
{
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "My API")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientSecrets =
{
new Secret("superSecretPassword".Sha256())
},
ClientId = "angular_spa",
ClientName = "Angular 4 Client",
AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials , //implicit
AllowedScopes = new List<string> { "openid", "profile", "userInfo", "api1" },
//AllowedScopes = new List<string> { StandardScopes.OpenId, StandardScopes.Profile, StandardScopes.Email },
RedirectUris = new List<string> {"http://localhost:4200/auth-callback", "http://localhost:4200/silent-refresh.html"},
PostLogoutRedirectUris = new List<string> { "http://localhost:4200/" },
AllowedCorsOrigins = new List<string> { "http://localhost:4200" },
AllowAccessTokensViaBrowser = true,
Enabled = true,
AllowOfflineAccess = true
}
};
}
public static List<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile() // <-- usefull
};
}
}
my project structure looks like this
it doesn't have any controllers. Should it have ?
UPDATE 2 looks like i figured out whats wrong.
The returnUrl
is not resolving properly on the POST
method. it is coming as the complete URL
. if i force it to a proper return URL
it works
var redirect_uri = HttpUtility.ParseQueryString(returnUrl).Get("redirect_uri");
I did as above and used variable 'redirect_uri' in Redirect function. it works but it looks like a hack. Should it automatically get the right thing ?
with this i get 'No state in response' error on Angular side and oidc-client
have no user after redirect.
UPDATE
Looks like I'm using some different nuget
package.
HttpContext.SignInAsync
has following constructors.
My HttpContext
seems to be defined in
Microsoft.AspNetCore.Mvc.RazorPages
looks like i have wrong Nugets or something. i am trying to supply a proper ClaimsPrincipal as well but not working.
I see there's a bit of confusion concerning the difference between the returnUrl and the redirect_uri. Although the end goal is a redirect to the client's redirect_uri, after authentication the client must actually redirect to the authorize endpoint for further processing (hence the reason why the url is different). You shouldn't need to change the returnUrl at all and can leave it the way it was.
The problem you're facing now is you're not calling HttpContext.SignInAsync
after a successful authentication. The SignInAsync
method is used to administer a cookie with the user's information that tells the endpoint at the returnUrl that the user was successfully authenticated, and it's okay to return a token to the redirect_uri. There are a lot of overloads for the SignInAsync
, but the one I find easiest to use is HttpContext.SignInAsync(string subject, params Claim[] claims)
. After doing this you should be able to finish the authentication.