在过去的几周里,我一直在为我们的一个系统开发一个新的身份提供者。这是我第一次使用IdentityServer和OpenId,并且我已经设法充分利用它,但是我一直在用这个打破一个混凝土墙。
我一直在尝试制作
我们的目标是拥有一个身份提供者(IdentityServer4),我们可以将其用于所有当前和未来的系统。我们希望这样做尽可能简单,因此它只验证用户,并为客户提供名称,profilepicture和电子邮件。
接下来,每个系统都有另一个IdentityServer4服务器,用于处理用户访问权限。这意味着,这将为客户提供角色等。
最后,我们有客户。
我试图说明,从长远来看我们希望拥有什么,但这个问题仅涉及ID。 PROVIDER,SYSTEM A IDSRV和SYSTEM A CLIENT 1。
|---- SYSTEM A CLIENT 1
|----SYSTEM A IDSRV ----|---- SYSTEM A CLIENT 2
| |---- SYSTEM A CLIENT 3
|
|
| |---- SYSTEM B CLIENT 1
ID. PROVIDER----|----SYSTEM B IDSRV ----|---- SYSTEM B CLIENT 2
| |---- SYSTEM B CLIENT 3
|
|
| |---- SYSTEM C CLIENT 1
|----SYSTEM C IDSRV ----|---- SYSTEM C CLIENT 2
|---- SYSTEM C CLIENT 3
最初的自动化工作非常完美!
我有什么
ID。提供者 - > SYSTEM A IDSRV的客户端配置
new Client
{
ClientId = "SystemA",
ClientSecrets = new List<Secret>
{
new Secret("SystemASecret".Sha256())
},
AllowedGrantTypes = GrantTypes.Hybrid,
AllowedScopes = new List<string>
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
StandardScopes.Email.Name,
StandardScopes.OfflineAccess.Name,
},
RedirectUris = new List<string>
{
"https://localhost:5400/signin-oidc"
},
PostLogoutRedirectUris = new List<string>
{
"http://localhost:5400"
},
RequireConsent = false,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
RefreshTokenExpiration = TokenExpiration.Absolute,
UpdateAccessTokenClaimsOnRefresh = true,
IdentityTokenLifetime = 30,
AccessTokenLifetime = 30,
AuthorizationCodeLifetime = 30,
AbsoluteRefreshTokenLifetime = 30,
SlidingRefreshTokenLifetime = 30,
}
SYSTEM A IDSRV - &gt; Startup.cs:
app.UseIdentityServer();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
AutomaticAuthenticate = false,
AutomaticChallenge = false,
LoginPath = new PathString("/Account/Login"),
ReturnUrlParameter = "returnUrl"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme,
SignOutScheme = IdentityServerConstants.SignoutScheme,
DisplayName = "ID. PROVIDER",
Authority = "https://localhost:5200",
ClientId = "SystemA",
ClientSecret = "SystemASecret",
ResponseType = "code id_token",
Scope =
{
"openid",
"profile",
"email",
"offline_access"
},
TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
},
GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true,
UseTokenLifetime = true
});
SYSTEM A IDSRV - &gt; SYSTEM A CLIENT 1的客户端配置:
new Client
{
ClientId = "SystemAClient1",
ClientName = "SYSTEM A CLIENT 1",
AllowedGrantTypes = GrantTypes.Hybrid,
RequireConsent = false,
ClientSecrets = new List<Secret>
{
new Secret("SystemAClient1Secret".Sha256())
},
RedirectUris = new List<string>
{
"https://localhost:5500/signin-oidc"
},
PostLogoutRedirectUris = new List<string>
{
"https://localhost:5500"
},
AllowedScopes = new List<string>
{
StandardScopes.OpenId.Name,
StandardScopes.Profile.Name,
StandardScopes.Email.Name,
StandardScopes.OfflineAccess.Name,
StandardScopes.Roles.Name,
CustomScopes.Custom1.Name,
CustomScopes.Custom2.Name,
CustomScopes.Custom3.Name
},
RefreshTokenUsage = TokenUsage.OneTimeOnly,
RefreshTokenExpiration = TokenExpiration.Absolute,
UpdateAccessTokenClaimsOnRefresh = true,
IdentityTokenLifetime = 30,
AccessTokenLifetime = 30,
AuthorizationCodeLifetime = 30,
AbsoluteRefreshTokenLifetime = 30,
SlidingRefreshTokenLifetime = 30
}
* SYSTEM A IDSRV - &gt; AccountController(登录时为ExternalCallback,受IdentityServer示例启发。)
public async Task<IActionResult> ExternalCallback(string returnUrl)
{
//Read external identity from the tempoary cookie
var tempUser = await HttpContext.Authentication.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (tempUser == null)
throw new Exception("External authentication error");
//Get external claims
var claims = tempUser.Claims.ToList();
//Extract UserId
var userIdClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (userIdClaim == null)
throw new Exception("Unknown UserId!");
//Move the UserId claim and from the claims collection to the UserId-property, and set the name of the external authentication provider
claims.Remove(userIdClaim);
var provider = userIdClaim.Issuer;
//Extract addition custom claims to store them in the database
var firstnameClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName);
if (firstnameClaim == null)
throw new Exception("Firstname claim missing");
var lastnameClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName);
if (lastnameClaim == null)
throw new Exception("Lastname claim missing!");
var emailClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email);
if (emailClaim == null)
throw new Exception("E-mail claim missing!");
var pictureClaim = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Picture);
if (pictureClaim == null)
throw new Exception("Picture claim missing!");
var user = await _userService.FindUserByEmail(emailClaim.Value);
if (user == null)
user = new User { Identities = new List<UserIdentity>() };
user.Email = emailClaim.Value;
user.Firstname = firstnameClaim.Value;
user.Lastname = lastnameClaim.Value;
user.Picture = pictureClaim.Value;
if (!user.Identities.Any(i => i.Identity == userIdClaim.Value && i.Provider == provider))
user.Identities.Add(new UserIdentity { Identity = userIdClaim.Value, Provider = provider });
// Save user and login
await _userService.SaveUser(user);
await HttpContext.Authentication.SignInAsync(userIdClaim.Value, emailClaim.Value, provider, claims.ToArray());
// Delete tempoary cookie used during external authentication
await HttpContext.Authentication.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
if (_interaction.IsValidReturnUrl(returnUrl))
return Redirect(returnUrl);
return Redirect("~/");
}
SYSTEM A CLIENT 1 - &gt; Startup.cs
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationScheme = "Cookies",
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
SignInScheme = "Cookies",
Authority = "https://localhost:5400",
ClientId = "SystemAClient1",
ClientSecret = "SystemAClient1Secret",
ResponseType = "code id_token",
Scope =
{
"openid",
"profile",
"email",
"offline_access"
},
GetClaimsFromUserInfoEndpoint = true,
SaveTokens = true,
UseTokenLifetime = true,
});
问题
当我在30秒刷新间隔后刷新SYSTEM A CLIENT 1上的页面时,我确实刷新了访问令牌并声明了SYSTEM A IDSRV应该如此。但该调用不会刷新访问令牌和ID上的声明。 PROVIDER。
我错过了什么?
提前感谢您阅读这篇文章,希望能帮到我! :)
TL;博士
当客户端启动访问令牌刷新时,IdentityServer4不会刷新来自外部提供程序的访问令牌。