Angular oidc-client and duende IdentityServer - Problem with authorize on client-site, user unauthorized

738 Views Asked by At

I'm learning angular and I find a problem that I didn't see anywhere else. To the point, when I'm trying to login to my client angular app with oidc-client via identityserver, It's working great up to a point. I'm redirecting to a identityserver, logged in, goes back to the login-callback, in server-side i'm authorized, but if I want to check if I'm authorized in client-side, I got a null user. If i want to logout, I got redirect to sign out in identityserver where I can see my token:
enter image description here

Server-side config.cs:

public class Config
    {
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Email(),
                new IdentityResources.Profile(),
                new IdentityResource("RedDot.API.read", new[] { JwtClaimTypes.Name, JwtClaimTypes.Email, "location" })
            };
        }

        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("RedDot.API.read", "Resource API")
                {
                    ApiSecrets =
                    {
                        new Secret("secret".Sha256())
                    },

                    UserClaims =
                    {
                        JwtClaimTypes.Name,
                        JwtClaimTypes.Email
                    },
                    Scopes = new List<string> { "RedDot.API.read" }
                }
            };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new[]
            {
                new Client {
                    RequireConsent = false,
                    ClientId = "reddot_ui",
                    ClientName = "RedDot",
                    AllowedGrantTypes = GrantTypes.Code,
                    AllowedScopes = 
                    { 
                        IdentityServerConstants.StandardScopes.OpenId, 
                        IdentityServerConstants.StandardScopes.Profile, 
                        "RedDot.API.read" 
                    },
                    RedirectUris = {"https://localhost:4200/login-callback"},
                    PostLogoutRedirectUris = {"https://localhost:4200/"},
                    AllowedCorsOrigins = {"https://localhost:4200"},
                    AllowAccessTokensViaBrowser = true,
                    AccessTokenLifetime = 3600,
                    ClientSecrets =
                    {
                        new Secret("rwebv832hvegfh49--l-w".Sha256())
                    },
                }
            };
        }
    }

Program.cs:

builder.Services.AddDbContext<AppIdentityDbContext>(options => options.UseSqlServer(dataConnectionString, conf => conf.MigrationsAssembly(assembly)));

builder.Services.AddIdentity<AppUser, IdentityRole>()
     .AddEntityFrameworkStores<AppIdentityDbContext>()
     .AddDefaultTokenProviders();

builder.Services.AddIdentityServer().AddDeveloperSigningCredential()
      .AddOperationalStore(options =>
      {
          options.ConfigureDbContext = builder => builder.UseSqlServer(dataConnectionString, conf => conf.MigrationsAssembly(assembly));
      })
      .AddInMemoryIdentityResources(Config.GetIdentityResources())
      .AddInMemoryApiResources(Config.GetApiResources())
      .AddInMemoryClients(Config.GetClients())
      .AddAspNetIdentity<AppUser>();

Client-side angular config:

private getUserManager() {
        if (!this._userManager) {
          const userManagerSettings: UserManagerSettings =
            new UserManagerSettings();
    
          userManagerSettings.authority = 'https://localhost:5443';
          userManagerSettings.client_id = 'reddot_ui';
          userManagerSettings.response_type = 'code';
          userManagerSettings.scope = 'openid profile RedDot.API.read'; 
          
          userManagerSettings.redirect_uri = 'https://localhost:4200/login-callback';
          userManagerSettings.post_logout_redirect_uri = 'https://localhost:4200/logout-callback';
    
          userManagerSettings.automaticSilentRenew = true;
          userManagerSettings.silent_redirect_uri = 'https://localhost:4200/silent-callback';
    
          userManagerSettings.userStore = new WebStorageStateStore({
            store: window.localStorage,
          }); // store information about Authentication in localStorage
    
          this._userManager = new UserManager(userManagerSettings);
    
          this._userManager.getUser().then((user) => {
            this._user = user;
            this.isUserDefined = true;
          });
        }
    }

Rest of authorize class in angular:

import { Injectable } from "@angular/core";
import { User, UserManager, WebStorageStateStore } from "oidc-client";
import { UserManagerSettings } from "../_models/usermanager.settings";

@Injectable()
export class AuthenticationService {
    isUserDefined = false;
    private _user: User | null;
    private _userManager: UserManager;

    isLoggedIn() {
        return this._user != null && !this._user.expired;
    }

    getName() {
        return this._user?.profile.nickname;
    }

    getAccessToken() {
        return this._user ? this._user.access_token : "";
    }

    getClaims() {
        return this._user?.profile;
    }

    startAuthentication() : Promise<void> {
        this.getUserManager();
        return this._userManager.signinRedirect();
    }

    completeAuthentication() {
        this.getUserManager();
        return this._userManager.signinRedirectCallback().then((user) => {
            this._user = user;
            this.isUserDefined = true;
        });
    }

    startLogout(): Promise<void> {
        this.getUserManager();
        return this._userManager.signoutRedirect();
    }

    completeLogout() {
        this.getUserManager();
        this._user = null;
        return this._userManager.signoutRedirectCallback();
    }


    silentSignInAuthentication() {
        this.getUserManager();
        return this._userManager.signinSilentCallback();
    }
}

Dunno where the problem can be and why I'm not authorized on client-side.

I've tried to change response type and protocol from http to https and conversely with no effect. Maybe somebody had the same problem.

1

There are 1 best solutions below

1
Chen On

The code you provided doesn't look like there is anything wrong. According to your description, you can log in and log out successfully, but you are not authorized. Does it mean that you cannot access the corresponding resources? Do you have any error messages?

I used https://demo.duendesoftware.com site to test with Angular application (here is the code of Angular), but did not reproduce your situation. You can see how the configuration items in it perform, and whether your configuration is missing some steps compared to it.

In addition, I found a sample code that is similar to your Angular program, but the Scope configuration is slightly different from yours. I am not sure if this is the reason, but you can use it as a reference