我的令牌缺少刷新和角色属性。我正在使用 OpenIddict。该代码直到今天仍然有效,并且仍然可以在家用计算机上使用,但不能在工作中使用。
我很确定我做错了什么,但由于我比较了 startup.cs、AuthorizationController.cs 并且它们是相同的(工作和家庭),我需要一些帮助,这可能是问题的根源。
我需要为登录的用户获取角色,因为我的 Angular2 应用程序需要知道用户可以在网页上做什么。
我发送的请求:
工作回应:
家庭回应:
启动代码(在家用电脑上也一样):
services.AddOpenIddict<int>()
.AddEntityFrameworkCoreStores<AppDbContext>()
.AddMvcBinders()
.EnableTokenEndpoint("/API/authorization/token")
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
.UseJsonWebTokens()
.AddEphemeralSigningKey() //todo naj bi bil pravi certifikat, če odstranič to vrstico ne dela in vidiš error.
.SetAccessTokenLifetime(TimeSpan.FromMinutes(30))
.SetRefreshTokenLifetime(TimeSpan.FromDays(14))
.DisableHttpsRequirement();
控制器代码(再次:在家用电脑上相同):
public class AuthorizationController : BaseController
{
public AuthorizationController(AppDbContext context, OpenIddictApplicationManager<OpenIddictApplication<int>> applicationManager, SignInManager<AppUser> signInManager, UserManager<AppUser> userManager) : base(context, applicationManager, signInManager, userManager)
{
}
[Authorize, HttpGet("authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
Debug.Assert(request.IsAuthorizationRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
// Retrieve the application details from the database.
var application = await applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted);
if (application == null)
{
return View("Error", new ErrorViewModel
{
Error = OpenIdConnectConstants.Errors.InvalidClient,
ErrorDescription = "Details concerning the calling client application cannot be found in the database"
});
}
// Flow the request_id to allow OpenIddict to restore
// the original authorization request from the cache.
return View(new AuthorizeViewModel
{
ApplicationName = application.DisplayName,
RequestId = request.RequestId,
Scope = request.Scope
});
}
[HttpPost("token"), Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
Debug.Assert(request.IsTokenRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
if (request.IsPasswordGrantType())
{
var user = await userManager.FindByNameAsync(request.Username);
if (user == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The email/password couple is invalid."
});
}
// Ensure the user is allowed to sign in.
if (!await signInManager.CanSignInAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The specified user is not allowed to sign in."
});
}
// Reject the token request if two-factor authentication has been enabled by the user.
if (userManager.SupportsUserTwoFactor && await userManager.GetTwoFactorEnabledAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The specified user is not allowed to sign in."
});
}
// Ensure the user is not already locked out.
if (userManager.SupportsUserLockout && await userManager.IsLockedOutAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
// Ensure the password is valid.
if (!await userManager.CheckPasswordAsync(user, request.Password))
{
if (userManager.SupportsUserLockout)
{
await userManager.AccessFailedAsync(user);
}
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The username/password couple is invalid."
});
}
if (userManager.SupportsUserLockout)
{
await userManager.ResetAccessFailedCountAsync(user);
}
// Create a new authentication ticket.
var ticket = await CreateTicketAsync(request, user);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
else if (request.IsRefreshTokenGrantType())
{
// Retrieve the claims principal stored in the refresh token.
var info = await HttpContext.Authentication.GetAuthenticateInfoAsync(
OpenIdConnectServerDefaults.AuthenticationScheme);
// Retrieve the user profile corresponding to the refresh token.
var user = await userManager.GetUserAsync(info.Principal);
if (user == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The refresh token is no longer valid."
});
}
// Ensure the user is still allowed to sign in.
if (!await signInManager.CanSignInAsync(user))
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidGrant,
ErrorDescription = "The user is no longer allowed to sign in."
});
}
// Create a new authentication ticket, but reuse the properties stored
// in the refresh token, including the scopes originally granted.
var ticket = await CreateTicketAsync(request, user, info.Properties);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
ErrorDescription = "The specified grant type is not supported."
});
}
private async Task<AuthenticationTicket> CreateTicketAsync(
OpenIdConnectRequest request, AppUser user,
AuthenticationProperties properties = null)
{
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await signInManager.CreateUserPrincipalAsync(user);
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
foreach (var claim in principal.Claims)
{
// In this sample, every claim is serialized in both the access and the identity tokens.
// In a real world application, you'd probably want to exclude confidential claims
// or apply a claims policy based on the scopes requested by the client application.
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(principal, properties,
OpenIdConnectServerDefaults.AuthenticationScheme);
if (!request.IsRefreshTokenGrantType())
{
// Set the list of scopes granted to the client application.
// Note: the offline_access scope must be granted
// to allow OpenIddict to return a refresh token.
ticket.SetScopes(new[] {
OpenIdConnectConstants.Scopes.OpenId,
OpenIdConnectConstants.Scopes.Email,
OpenIdConnectConstants.Scopes.Profile,
OpenIdConnectConstants.Scopes.OfflineAccess,
OpenIddictConstants.Scopes.Roles
}.Intersect(request.GetScopes()));
}
ticket.SetResources("OpPISWeb"); //also in startup.cs
return ticket;
}
}
为了解码 id_token 我正在使用angular-jwt:
return this.http.post('api/authorization/token', this.encodeObjectToParams(data), options)
.map(res => res.json())
.map((tokens: AuthTokenModel) =>
{
console.log("loged in", tokens);
let now = new Date();
tokens.expiration_date = new Date(now.getTime() + tokens.expires_in * 1000).getTime().toString();
localStorage.setItem('id_token', tokens.access_token);
localStorage.setItem('refresh_token', tokens.refresh_token);
const profile = this.jwtHelper.decodeToken(tokens.id_token) as ProfileModel;
const roles: string[] = typeof profile.role === "string" ? [profile.role] : profile.role;
const userProfile: Profile = new Profile(parseInt(profile.sub), roles);
localStorage.setItem('profile', JSON.stringify(userProfile));
this.refreshTokens(tokens.expires_in * 1000 * 0.8);
return profile;
});