2016-05-18 53 views
11

我正在嘗試使用授權碼流在AspNet Core上實現Identity Server 4。身份服務器4授權碼流程示例

事情是,github上的IdentityServer4存儲庫有幾個樣本,但沒有授權碼流程

是否有人有關於如何實施的授權代碼流程與身份驗證服務器4和MVC中的客戶端消費它?

回答

21

這裏有一個授權碼流與身份服務器4和MVC客戶端來使用它的實現。

IdentityServer4可以使用client.cs文件註冊我們的MVC客戶端,它的Client,ClientSecret,允許的許可類型(授權碼在這種情況下),和我們的客戶的RedirectUri:

public class Clients 
{ 
    public static IEnumerable<Client> Get() 
    { 
     var secret = new Secret { Value = "mysecret".Sha512() }; 

     return new List<Client> { 
      new Client { 
       ClientId = "authorizationCodeClient2", 
       ClientName = "Authorization Code Client", 
       ClientSecrets = new List<Secret> { secret }, 
       Enabled = true, 
       AllowedGrantTypes = new List<string> { "authorization_code" }, //DELTA //IdentityServer3 wanted Flow = Flows.AuthorizationCode, 
       RequireConsent = true, 
       AllowRememberConsent = false, 
       RedirectUris = 
        new List<string> { 
         "http://localhost:5436/account/oAuth2" 
        }, 
       PostLogoutRedirectUris = 
        new List<string> {"http://localhost:5436"}, 
       AllowedScopes = new List<string> { 
        "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(); 
    } 

作爲參考,這裏是上面提到的用戶和作用域類:

public static class Users 
{ 
    public static List<InMemoryUser> Get() 
    { 
     return new List<InMemoryUser> { 
      new InMemoryUser { 
       Subject = "1", 
       Username = "user", 
       Password = "pass123", 
       Claims = new List<Claim> { 
        new Claim(ClaimTypes.GivenName, "GivenName"), 
        new Claim(ClaimTypes.Surname, "surname"), //DELTA //.FamilyName in IdentityServer3 
        new Claim(ClaimTypes.Email, "[email protected]"), 
        new Claim(ClaimTypes.Role, "Badmin") 
       } 
      } 
     }; 
    } 
} 

public class Scopes 
{ 
    // scopes define the resources in your system 
    public static IEnumerable<Scope> Get() 
    { 
     return new List<Scope> { 
      new Scope 
      { 
       Name = "api", 
       DisplayName = "api scope", 
       Type = ScopeType.Resource, 
       Emphasize = false, 
      } 
     }; 
    } 
} 

MVC應用程序需要兩種控制器方法。第一種方法啓動服務提供商(SP-Initiated)工作流程。它創建一個狀態值,將其保存在基於Cookie的身份驗證中間件中,然後將瀏覽器重定向到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 
} 

作爲參考,這裏的常數和即時存檔方法利用以上:

//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); 
} 

第二MVC動作方法由IdenityServer4用戶輸入他們的憑證,並檢查任何授權盒之後被調用。操作方法:

  • 拿過授權碼和國家從查詢字符串
  • 確定該狀態
  • 回發到IdentityServer4交換授權代碼的訪問令牌

這裏的方法:

[HttpGet] 
public async Task<ActionResult> 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<string, string>("grant_type", grantType) 
      ,new KeyValuePair<string, string>("code", authorizationCode) 
      ,new KeyValuePair<string, string>("redirect_uri", redirectUri) 
      ,new KeyValuePair<string, string>("client_id", clientId)    //consider sending via basic authentication header 
      ,new KeyValuePair<string, string>("client_secret", clientSecret) 
     }); 
     var httpResponseMessage = client.PostAsync(idPServerTokenUriFragment, content).Result; 
     json = httpResponseMessage.Content.ReadAsStringAsync().Result; 
    } 

    //Extract the Access Token 
    dynamic results = JsonConvert.DeserializeObject<dynamic>(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 (e.g. [Authorized] attribute) 
    this.Request.GetOwinContext().Authentication.SignIn(id); 

    return this.Redirect("/Home"); 
} 

檢查收到的州是否您期望的幫助抵禦CSRF攻擊:

private async Task<AuthenticateResult> 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方法使用微軟的System.IdentityModel和System.IdentityModel.Tokens.Jwt:http://www.twobotechnologies.com/blog/2014/02/importance-of-state-in-oauth2.html

這ValidateStateAsync方法收到的國家進行比較,以什麼在Cookie中保存中間件關閉圖書館檢查智威湯遜是否正確簽署。

private IEnumerable<Claim> 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在https://github.com/bayardw/IdentityServer4.Authorization.Code

+2

Woa!謝謝你的完整答案! –

+3

我遇到了很多困難,似乎所有的教程/答案似乎都使​​用了一些較舊的IdentityServer形式。我知道這是直接針對版本4的問題;但是,即使在他們的文檔頁面上也有差異。例如,它們不再具有AddInMemoryScopes,AddImMemoryUsers和沒有標準的User類。 http://docs.identityserver.io/en/release/configuration/startup.html – Adrian

4

下面是一個示例 - 它使用混合流代替代碼流。但是如果你的客戶端庫支持它(和aspnetcore中間件一樣),混合流量更值得推薦。

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

+5

你的鏈接斷開。這就是爲什麼你應該把所有重要的部分放在答案本身。 –

+0

嗨,多米尼克,我認爲[這](https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/5_HybridFlowAuthenticationWithApiAccess)是一個更好的鏈接,現在使用? – DavidG