Identity Server 4 is not redirecting to Angular app after login

10,420

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.

Share:
10,420

Related videos on Youtube

Raas Masood
Author by

Raas Masood

Updated on June 04, 2022

Comments

  • Raas Masood
    Raas Masood almost 2 years

    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

    enter image description here

    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

    enter image description here

               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.

    enter image description here

    • Fateh Mohamed
      Fateh Mohamed about 5 years
      hav you tried this 'localhost:4200/auth-callback#' as redirect url
    • penleychan
      penleychan about 5 years
      Could you try seting AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials to just AllowedGrantTypes = GrantTypes.Implicit?
    • Raas Masood
      Raas Masood about 5 years
      # at the end of url gives page not found error.
    • Raas Masood
      Raas Masood about 5 years
      i also tried GrantTypes.Implicit not change
    • Raas Masood
      Raas Masood about 5 years
      @penleychan and Fateh check the second update. it might help you to figureout whats wrong.
  • Raas Masood
    Raas Masood about 5 years
    Not at my machine right not but so eager to test this. This totally makes sense. Ill let you know in a but
  • Raas Masood
    Raas Masood about 5 years
    please check the update. any idea how to supply a proper claims principal? also note that claims are coming out to be 0 because while registering it looks like i am not adding claims to users.
  • Randy
    Randy about 5 years
    You should be able to use the SignInAsync extension method with the IdentityServer4 package installed (and the implementation for IdentityServer4 does a little more than the regular extension method). If you'd like to call SignInAsync with the regular ASP.NET Core extension method for testing you can call new ClaimsPrincipal(new ClaimsIdentity(claims)) where claims is your list of claims. As for the reason of why there's no claims belonging to the signed in user is all dependent on the claims that go into the SignInAsync method
  • Anurag
    Anurag almost 2 years
    @Randy You saved my life !! I had a similar problem like this. My client was native xamarin client and after login the user shows not authenticated (on event viewer). I was using a custom signing manager and not authenticating the HTTP context. the user was valid and the token endpoint was not returned. By this answer you save my time dear.. I appreciate lot. Keep helping people. Thanks one again.