Identity Server 4授权代码流示例

我正在尝试使用授权代码流实现带有AspNet Core的Identity Server 4。

问题是,github上的IdentityServer4存储库有几个样本,但没有一个带有授权代码流

有没有人有关于如何使用Identity Server 4和MVC中的客户端实现授权代码流的示例?

以下是Identity Server 4和MVC客户端使用授权代码流的实现。

IdentityServer4可以使用client.cs文件注册我们的MVC客户端,它是ClientId,ClientSecret,允许的授权类型(在本例中为授权代码),以及我们客户端的RedirectUri:

public class Clients { public static IEnumerable Get() { var secret = new Secret { Value = "mysecret".Sha512() }; return new List { new Client { ClientId = "authorizationCodeClient2", ClientName = "Authorization Code Client", ClientSecrets = new List { secret }, Enabled = true, AllowedGrantTypes = new List { "authorization_code" }, //DELTA //IdentityServer3 wanted Flow = Flows.AuthorizationCode, RequireConsent = true, AllowRememberConsent = false, RedirectUris = new List { "http://localhost:5436/account/oAuth2" }, PostLogoutRedirectUris = new List {"http://localhost:5436"}, AllowedScopes = new List { "api" }, AccessTokenType = AccessTokenType.Jwt } }; } } 

IdentityServer4项目中的Startup.cs的ConfigurationServices方法中引用了此类:

  public void ConfigureServices(IServiceCollection services) { ////Grab key for signing JWT signature ////In prod, we'd get this from the certificate store or similar var certPath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "SscSign.pfx"); var cert = new X509Certificate2(certPath); // configure identity server with in-memory stores, keys, clients and scopes services.AddDeveloperIdentityServer(options => { options.IssuerUri = "SomeSecureCompany"; }) .AddInMemoryScopes(Scopes.Get()) .AddInMemoryClients(Clients.Get()) .AddInMemoryUsers(Users.Get()) .SetSigningCredential(cert); services.AddMvc(); } 

作为参考,以下是上面引用的Users和Scopes类:

 public static class Users { public static List Get() { return new List { new InMemoryUser { Subject = "1", Username = "user", Password = "pass123", Claims = new List { new Claim(ClaimTypes.GivenName, "GivenName"), new Claim(ClaimTypes.Surname, "surname"), //DELTA //.FamilyName in IdentityServer3 new Claim(ClaimTypes.Email, "user@somesecurecompany.com"), new Claim(ClaimTypes.Role, "Badmin") } } }; } } public class Scopes { // scopes define the resources in your system public static IEnumerable Get() { return new List { new Scope { Name = "api", DisplayName = "api scope", Type = ScopeType.Resource, Emphasize = false, } }; } } 

MVC应用程序需要两种控制器方法。 第一种方法启动了服务提供商(SP-Initiated)工作流程。 它创建一个State值,将其保存在基于cookie的身份validation中间件中,然后将浏览器重定向到IdentityProvider(IdP) – 在这种情况下我们的IdentityServer4项目。

 public ActionResult SignIn() { var state = Guid.NewGuid().ToString("N"); //Store state using cookie-based authentication middleware this.SaveState(state); //Redirect to IdP to get an Authorization Code var url = idPServerAuthUri + "?client_id=" + clientId + "&response_type=" + response_type + "&redirect_uri=" + redirectUri + "&scope=" + scope + "&state=" + state; return this.Redirect(url); //performs a GET } 

作为参考,这里是上面使用的常量和SaveState方法:

 //Client and workflow values private const string clientBaseUri = @"http://localhost:5436"; private const string validIssuer = "SomeSecureCompany"; private const string response_type = "code"; private const string grantType = "authorization_code"; //IdentityServer4 private const string idPServerBaseUri = @"http://localhost:5000"; private const string idPServerAuthUri = idPServerBaseUri + @"/connect/authorize"; private const string idPServerTokenUriFragment = @"connect/token"; private const string idPServerEndSessionUri = idPServerBaseUri + @"/connect/endsession"; //These are also registered in the IdP (or Clients.cs of test IdP) private const string redirectUri = clientBaseUri + @"/account/oAuth2"; private const string clientId = "authorizationCodeClient2"; private const string clientSecret = "mysecret"; private const string audience = "SomeSecureCompany/resources"; private const string scope = "api"; //Store values using cookie-based authentication middleware private void SaveState(string state) { var tempId = new ClaimsIdentity("TempCookie"); tempId.AddClaim(new Claim("state", state)); this.Request.GetOwinContext().Authentication.SignIn(tempId); } 

用户输入凭据并检查任何授权框后,IdenityServer4将调用第二个MVC操作方法。 动作方法:

  • 从查询字符串中获取授权代码和状态
  • validation状态
  • POST回IdentityServer4以交换访问令牌的授权码

这是方法:

 [HttpGet] public async Task oAuth2() { var authorizationCode = this.Request.QueryString["code"]; var state = this.Request.QueryString["state"]; //Defend against CSRF attacks http://www.twobotechnologies.com/blog/2014/02/importance-of-state-in-oauth2.html await ValidateStateAsync(state); //Exchange Authorization Code for an Access Token by POSTing to the IdP's token endpoint string json = null; using (var client = new HttpClient()) { client.BaseAddress = new Uri(idPServerBaseUri); var content = new FormUrlEncodedContent(new[] { new KeyValuePair("grant_type", grantType) ,new KeyValuePair("code", authorizationCode) ,new KeyValuePair("redirect_uri", redirectUri) ,new KeyValuePair("client_id", clientId) //consider sending via basic authentication header ,new KeyValuePair("client_secret", clientSecret) }); var httpResponseMessage = client.PostAsync(idPServerTokenUriFragment, content).Result; json = httpResponseMessage.Content.ReadAsStringAsync().Result; } //Extract the Access Token dynamic results = JsonConvert.DeserializeObject(json); string accessToken = results.access_token; //Validate token crypto var claims = ValidateToken(accessToken); //What is done here depends on your use-case. //If the accessToken is for calling a WebAPI, the next few lines wouldn't be needed. //Build claims identity principle var id = new ClaimsIdentity(claims, "Cookie"); //"Cookie" matches middleware named in Startup.cs //Sign into the middleware so we can navigate around secured parts of this site (eg [Authorized] attribute) this.Request.GetOwinContext().Authentication.SignIn(id); return this.Redirect("/Home"); } 

检查国家收到的是你所期望的有助于抵御CSRF攻击: http : //www.twobotechnologies.com/blog/2014/02/importance-of-state-in-oauth2.html

此ValidateStateAsync方法将收到的状态与cookie中间件中保存的状态进行比较:

 private async Task ValidateStateAsync(string state) { //Retrieve state value from TempCookie var authenticateResult = await this.Request .GetOwinContext() .Authentication .AuthenticateAsync("TempCookie"); if (authenticateResult == null) throw new InvalidOperationException("No temp cookie"); if (state != authenticateResult.Identity.FindFirst("state").Value) throw new InvalidOperationException("invalid state"); return authenticateResult; } 

此ValidateToken方法使用Microsoft的System.IdentityModel和System.IdentityModel.Tokens.Jwt库来检查JWT是否已正确签名。

 private IEnumerable ValidateToken(string token) { //Grab certificate for verifying JWT signature //IdentityServer4 also has a default certificate you can might reference. //In prod, we'd get this from the certificate store or similar var certPath = Path.Combine(Server.MapPath("~/bin"), "SscSign.pfx"); var cert = new X509Certificate2(certPath); var x509SecurityKey = new X509SecurityKey(cert); var parameters = new TokenValidationParameters { RequireSignedTokens = true, ValidAudience = audience, ValidIssuer = validIssuer, IssuerSigningKey = x509SecurityKey, RequireExpirationTime = true, ClockSkew = TimeSpan.FromMinutes(5) }; //Validate the token and retrieve ClaimsPrinciple var handler = new JwtSecurityTokenHandler(); SecurityToken jwt; var id = handler.ValidateToken(token, parameters, out jwt); //Discard temp cookie and cookie-based middleware authentication objects (we just needed it for storing State) this.Request.GetOwinContext().Authentication.SignOut("TempCookie"); return id.Claims; } 

包含这些源文件的工作解决方案驻留在GitHub上, url为https://github.com/bayardw/IdentityServer4.Authorization.Code

这是一个示例 – 它使用混合流而不是代码流。 但是,如果客户端库支持混合流(并且aspnetcore中间件支持),则更推荐混合流。

https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/5_HybridFlowAuthenticationWithApiAccess