[ASP.NET Core] 初見 IdentityServer4:密碼授權模式、刷新AccessToken

繼上一篇的 客戶授權模式,這篇會繼續實現 密碼授權模式以及 刷新 AccessToken 的方式

密碼授權模式

客戶授權模式 的差異在參數增加了 username 以及 password,在取得token時一併驗證 username 及 password,並且在 JWT 中添加自定義的資訊,比如說 Roles

username / password 檢查

實務上會實際存取資料庫,驗證並返回相關會員的資料,為了方便這邊僅寫死從程式碼內做判斷及檢查

實作 IResourceOwnerPasswordValidator

public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
    public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
    {
        if (context.Request.Raw["password"] != "password" ||
            context.Request.Raw["username"] != "username")
        {
            return Task.CompletedTask;
        }

        context.Result.IsError = false;
        context.Result.Subject = GetClaimsPrincipal();

        return Task.CompletedTask;
    }

    private ClaimsPrincipal GetClaimsPrincipal()
    {
        var issued = DateTimeOffset.Now.ToUnixTimeSeconds();

        var claims = new List<Claim>
        {
            new Claim(JwtClaimTypes.Subject, Guid.NewGuid().ToString()),
            new Claim(JwtClaimTypes.AuthenticationTime, issued.ToString()),
            new Claim(JwtClaimTypes.IdentityProvider, "localhost"),
        };

        return new ClaimsPrincipal(new ClaimsIdentity(claims));
    }
}

調整 Startup

加上最後一行,AddResourceOwnerValidator

services.AddIdentityServer()
    .AddInMemoryApiResources(IdentityConfig.GetApiResources())
    .AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources())
    .AddInMemoryClients(IdentityConfig.GetClients())
    .AddInMemoryApiScopes(IdentityConfig.GetScopes())
    .AddDeveloperSigningCredential()
    .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>();

在JWT加入自定義的內容

實作 IProfileService

public class CustomProfileService : IProfileService
{
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        context.IssuedClaims.AddRange(new List<Claim>()
        {
            new Claim(JwtClaimTypes.Role, "admin")
        });

        return Task.CompletedTask;
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        context.IsActive = true;

        return Task.CompletedTask;
    }
}

調整 Startup

加上最後一行,CustomProfileService

services.AddIdentityServer()
    .AddInMemoryApiResources(IdentityConfig.GetApiResources())
    .AddInMemoryIdentityResources(IdentityConfig.GetIdentityResources())
    .AddInMemoryClients(IdentityConfig.GetClients())
    .AddInMemoryApiScopes(IdentityConfig.GetScopes())
    .AddDeveloperSigningCredential(false)
    .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
    .AddProfileService<CustomProfileService>();

調整 IdentityConfig

將 Client 的 AllowedGrantTypes 更改為 “GrantTypes.ResourceOwnerPassword”

執行

request的參數增加了 username 和 password,而 grant_type 更改為 “password”

密碼錯誤時則無法取得 token

檢查 JWT 可以看到增加了自定義的內容

若 Authrize 有限制 Roles 時,也是會有作用的


Refresh AccessToken

調整 IdentityServerConfig

調整 Client

  1. 在 Scope 加上 IdentityServerConstants.StandardScopes.OfflineAccess
  2. 加上 token 相關的參數(有效時間、使用RefreshToken、過期方式…等等)
new Client
{
    ClientId = "client",
    AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
    ClientSecrets =
    {
        new Secret("secret".Sha256()),
    },
    AllowedScopes =
    {
        "MyApi",
        IdentityServerConstants.StandardScopes.OfflineAccess
    },
    AllowOfflineAccess = true,
    RefreshTokenUsage = TokenUsage.ReUse,
    AccessTokenLifetime = 18000,
    RefreshTokenExpiration = TokenExpiration.Absolute,
    AbsoluteRefreshTokenLifetime = 300,
},

執行

取得 token 時會一併返回 refreshToken

拿 refreshToken 可以取的新的 AccessToken(注意這邊用的是不同的 grant_type)


花了幾天玩了一下 IdentityServer,筆記一下整個過程,之後有時間再補上連接 SQL Server的做法

SampleCode

IdentityServer4

IdentityServer4 官方文檔用到的Templates

晓晨Master Blog