Identity Server 3を使用して、angularクライアントのアクセス/更新トークンを認証および生成しています。
現在Angularクライアントの48時間で期限切れになるように更新トークンを設定しています。
My Angularアプリケーションを使用する一部のユーザーは、資格情報を再入力せずに100日間サインオンする必要があります。特定のユーザーの更新トークンの有効期限を設定することは可能ですか?クライアント全体ではなく?
私のデータベースには100人のユーザーがいますが、残りのユーザーは48時間ごとに認証を行う必要がありますが、特定の1人のユーザーだけが100日以内に再認証する必要がないようにしたいと考えています。
以下に沿ったもの:
if (user == "Super Man") {
AbsoluteRefreshTokenLifetime = TimeSpan.FromDays(100.0).Seconds,
}
これは可能ですか?または、クライアント全体の更新トークンの有効期限の設定のみに制限されていますか?
ありがとうございました
私はIdentityServer3で作業したことがなく、以下のコードもテストしていませんが、コンセプトはうまくいくと思います。
IdentityServer3のコードを見ると、 DefaultRefreshTokenService.CreateRefreshTokenAsync でライフタイムが設定されていることがわかります。
int lifetime;
if (client.RefreshTokenExpiration == TokenExpiration.Absolute)
{
Logger.Debug("Setting an absolute lifetime: " + client.AbsoluteRefreshTokenLifetime);
lifetime = client.AbsoluteRefreshTokenLifetime;
}
else
{
Logger.Debug("Setting a sliding lifetime: " + client.SlidingRefreshTokenLifetime);
lifetime = client.SlidingRefreshTokenLifetime;
}
コアコードを変更する必要はありませんが、独自の実装でIRefreshTokenServiceをオーバーライドできるはずです。
例として CustomUserService sample からコードを取得すると:
internal class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/core", coreApp =>
{
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
var refreshTokenService = new MyDefaultRefreshTokenService();
// note: for the sample this registration is a singletone (not what you want in production probably)
factory.RefreshTokenService = new Registration<IrefreshTokenService>(resolver => refreshTokenService);
MyDefaultRefreshTokenServiceは、DefaultRefreshTokenServiceのコピーです。
コンパイルするには、IdentityModel(v1.13.1)のNuGetパッケージを追加し、次のクラスを追加します。
using System;
namespace IdentityServer3.Core.Extensions
{
internal static class DateTimeOffsetHelper
{
internal static Func<DateTimeOffset> UtcNowFunc = () => DateTimeOffset.UtcNow;
internal static DateTimeOffset UtcNow
{
get
{
return UtcNowFunc();
}
}
internal static int GetLifetimeInSeconds(this DateTimeOffset creationTime)
{
return (int)(UtcNow - creationTime).TotalSeconds;
}
}
}
現在、イベントに関するいくつかのコンパイルエラーがあります。コードをテストするためにイベントを削除できます。機能する場合は、いつでも追加することを選択できます。
そして今度は、ユーザーごとにRefreshTokenLifetimeを実装します。お使いのバージョンのRefreshTokenServiceでは、クライアントコードを削除し、独自のロジックを使用してユーザーごとのライフタイムを決定できます。
件名は入手可能ですが、すでに十分な情報が含まれているかどうかはわかりません。ただし、その場合は、userManagerにアクセスしてストアから存続期間を読み取ることができます。または、代替情報を使用して有効期間情報を渡します(おそらく、有効期間値を含むクレームを使用できます)。
繰り返しますが、これはテストしませんでしたが、コンセプトは機能するはずです。
たとえば、スライドセッションを検討してください。スライディングセッションでは、ユーザーが行ったすべての認証済みアクションを使用して、有効期間が短い新しいトークンを送信します。 ユーザーがアクティブである限り、ユーザーは認証されたままです(たとえば、トークン管理の実装が必要ですが、有効期限が切れる前にユーザーの操作が必要です)。ユーザーが期限切れのトークンを送信する場合、それは彼がしばらく非アクティブであることを意味します。
JWTの仕組みを見てみましょう:
JWTは主に次の場合に適しています。
したがって、セッションでのJWTの使用を停止 、ほとんどの場合、セッショントークンとしてJWTを使用することはお勧めできません 。
JWTを更新する場合、 JWT更新トークンと.NET Core は、独自のコードと JWT(JSON Web Token)内の説明を実装するのに役立つ場合があります。 は、作業シナリオを設計するためのガイドです。操作を更新する前に、目的のユーザーを検査する必要があります。
Handle Refresh Token Using ASP.NET Core 2.0 And JSON Web Token の別の実装が見つかりました。
私はMicrosoftのIdentity Serverに精通していません(以下のコードで参照している「Identity Service」はカスタム実装です)が、HTTPヘッダーのトークンをインターセプトするための認証ハンドラーの作成を検討し、トークンのプレフィックスを調べてから、正常に処理するか、寿命を延ばすかを決定します。
私の場合、JWTがトークンを処理する前にトークンをインターセプトします。 (私はSharePointワークフローの制限を回避するためにこれを行わなければなりませんでした。ああ、SharePointです。)これがAuthenticationHandlerクラスです:
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
namespace CompanyName.Core2.Application.Middleware
{
[UsedImplicitly]
public class AuthenticationHandler : AuthenticationHandler<AuthenticationOptions>
{
public const string AuthenticationScheme = "CompanyName Token";
[UsedImplicitly] public const string HttpHeaderName = "Authorization";
[UsedImplicitly] public const string TokenPrefix = "CompanyName ";
public AuthenticationHandler(IOptionsMonitor<AuthenticationOptions> Options, ILoggerFactory Logger, UrlEncoder Encoder, ISystemClock Clock)
: base(Options, Logger, Encoder, Clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(HttpHeaderName, out StringValues authorizationValues))
{
// Indicate failure.
return await Task.FromResult(AuthenticateResult.Fail($"{HttpHeaderName} header not found."));
}
string token = authorizationValues.ToString();
foreach (AuthenticationIdentity authenticationIdentity in Options.Identities)
{
if (token == $"{TokenPrefix}{authenticationIdentity.Token}")
{
// Authorization token is valid.
// Create claims identity, add roles, and add claims.
ClaimsIdentity claimsIdentity = new ClaimsIdentity(AuthenticationScheme);
claimsIdentity.AddClaim(new Claim(ClaimTypes.Name, authenticationIdentity.Username));
foreach (string role in authenticationIdentity.Roles)
{
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role));
}
foreach (string claimType in authenticationIdentity.Claims.Keys)
{
string claimValue = authenticationIdentity.Claims[claimType];
claimsIdentity.AddClaim(new Claim(claimType, claimValue));
}
// Create authentication ticket and indicate success.
AuthenticationTicket authenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), Scheme.Name);
return await Task.FromResult(AuthenticateResult.Success(authenticationTicket));
}
}
// Indicate failure.
return await Task.FromResult(AuthenticateResult.Fail($"Invalid {HttpHeaderName} header."));
}
}
}
次に、サービスのStartupクラスに、使用する認証ハンドラーを決定するコードを追加します。ここでの重要な機能はForwardDefaultSelectorです。
public void ConfigureServices(IServiceCollection Services)
{
// Require authentication token.
// Enable CompanyName token for SharePoint workflow client, which cannot pass HTTP headers > 255 characters (JWT tokens are > 255 characters).
// Enable JWT token for all other clients. The JWT token specifies the security algorithm used when it was signed (by Identity service).
Services.AddAuthentication(AuthenticationHandler.AuthenticationScheme).AddCompanyNameAuthentication(Options =>
{
Options.Identities = Program.AppSettings.AuthenticationIdentities;
Options.ForwardDefaultSelector = HttpContext =>
{
// Forward to JWT authentication if CompanyName token is not present.
string token = string.Empty;
if (HttpContext.Request.Headers.TryGetValue(AuthenticationHandler.HttpHeaderName, out StringValues authorizationValues))
{
token = authorizationValues.ToString();
}
return token.StartsWith(AuthenticationHandler.TokenPrefix)
? AuthenticationHandler.AuthenticationScheme
: JwtBearerDefaults.AuthenticationScheme;
};
})
.AddJwtBearer(Options =>
{
Options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Program.AppSettings.ServiceOptions.TokenSecret)),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(_clockSkewMinutes)
};
});
AuthenticationBuilderクラスに拡張メソッドを追加します。
public static AuthenticationBuilder AddCompanyNameAuthentication(this AuthenticationBuilder AuthenticationBuilder, Action<AuthenticationOptions> ConfigureOptions = null)
{
return AuthenticationBuilder.AddScheme<AuthenticationOptions, AuthenticationHandler>(AuthenticationHandler.AuthenticationScheme, ConfigureOptions);
}
必要に応じて、認証オプション。
using JetBrains.Annotations;
using Microsoft.AspNetCore.Authentication;
namespace CompanyName.Core2.Application.Middleware
{
public class AuthenticationOptions : AuthenticationSchemeOptions
{
[UsedImplicitly]
public AuthenticationIdentities Identities { get; [UsedImplicitly] set; }
public AuthenticationOptions()
{
Identities = new AuthenticationIdentities();
}
}
}
AuthenticationIdentitiesは、トークンをユーザー名、ロール、およびクレーム(SharePointワークフローエンジンのトークン)に関連付けるために定義したクラスです。 appsettings.jsonから読み込まれます。ほとんどの場合、オプションクラスには、有効期間の延長が許可されているユーザーのリストが含まれます。
using System.Collections.Generic;
using JetBrains.Annotations;
namespace CompanyName.Core2.Application.Middleware
{
public class AuthenticationIdentity
{
public string Token { get; [UsedImplicitly] set; }
public string Username { get; [UsedImplicitly] set; }
[UsedImplicitly] public List<string> Roles { get; [UsedImplicitly] set; }
[UsedImplicitly] public Dictionary<string, string> Claims { get; [UsedImplicitly] set; }
public AuthenticationIdentity()
{
Roles = new List<string>();
Claims = new Dictionary<string, string>();
}
}
}