-

   rss_rss_hh_new

 - e-mail

 

 -

 LiveInternet.ru:
: 17.03.2011
:
:
: 51

:


[ ] OpenId Connect ASP.NET Core IdentityServer4 oidc-client

, 13 2017 . 00:00 +
NickT 00:00

OpenId Connect ASP.NET Core IdentityServer4 oidc-client


, OpenId Connect ASP.NET Core. , , , . , , , OpenId Connect Implicit Flow ASP.NET Core, , .


, , . . , .


OpenId Connect


OpenId Connect, .


OpenId Connect ( OpenId) , OAuth2.0. , OAuth2 , . OpenID Connect , claims. OpenId Connect UserInfo endpoint, . JSON Web Token (JWT), .


, Connect2id, Auth0 Stormpath. , - , .


Identity Server , , .



OpenId Connect Implicit Flow, JavaScript-, , SPA. , , . , OpenId Connect, , .



  • IdentityServer4
  • oidc-client

.



3 :


  1. IdentityServer OpenId Connect.
  2. Api -.
  3. Client JavaScript, JavaScriptClient.

: Client IdentityServer access_token (JWT), Bearer- - Api.


OpenId Connect . Flow.
Implicit Flow, , :


  1. , .
  2. .
  3. .
  4. .
  5. id_token' , , access_token'.
  6. id_token Subject Identifier .

Implicit Flow



, , , Quickstart.


Api IdentityServer dotnet run IdentityServer , .


, , .


. , Visual Studio 2017 (15.3).
solution OpenIdConnectSample.


IdentityServer, , , , , .


, Implicit Flow.


1. IdentityServer


solution , ASP.NET Core 1.1.


NuGet-


Install-Package Microsoft.AspNetCore.Mvc -Version 1.1.3
Install-Package Microsoft.AspNetCore.StaticFiles -Version 1.1.2
Install-Package IdentityServer4 -Version 1.5.2

, .. Install-Package . IdentityServer Asp.NET Core 2.0 dev-, , Quickstart UI. .NET Core 1.1 2.0 .


Main Program.cs ,


public static void Main(string[] args)
{
    Console.Title = "IdentityServer";

    // https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?tabs=aspnetcore2x
    var host = new WebHostBuilder()
        .UseKestrel()
        //  ,     Kestrel  
        .UseUrls("http://localhost:5000")
        //    UI - 
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup()
        .Build();

    host.Run();
}

Startup.cs



  1. using System.Security.Claims;
    using IdentityServer4;
    using IdentityServer4.Configuration;
    using IdentityServer4.Models;
    using IdentityServer4.Test;
  2. , IdentityServer, . ConfigureServices. , .


public static IEnumerable GetIdentityResources()
{
    // ,  scopes   IdentityServer
    return new List
    {
        // "sub" claim
        new IdentityResources.OpenId(),
        //  claims    profile scope
        // http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
        new IdentityResources.Profile(),
    };
}

claim sub, OpenId Connect, claim scope profile, OpenId Connect , , .


, API


public static IEnumerable GetApiResources()
{
    // claims  scopes    access_token
    return new List
    {
        //  scope "api1"  IdentityServer
        new ApiResource("api1", "API 1", 
            //  claims   scope api1
            new[] {"name", "role" })
    };
}     

,


public static IEnumerable GetClients()
{
    return new List
    {
        new Client
        {
            //  ,   client_id     
            ClientId = "js",
            ClientName = "JavaScript Client",
            AllowedGrantTypes = GrantTypes.Implicit,
            AllowAccessTokensViaBrowser = true,
            //      , 
            //  false      UserInfo endpoint
            AlwaysIncludeUserClaimsInIdToken = true,
            //         
            //  User Agent,   
            RedirectUris = {
                //    
                "http://localhost:5003/callback.html",
                //      access_token  iframe
                "http://localhost:5003/callback-silent.html"
            },
            PostLogoutRedirectUris = { "http://localhost:5003/index.html" },
            //   ,     CORS-
            AllowedCorsOrigins = { "http://localhost:5003" },
            //  scopes,      
            AllowedScopes =
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                "api1"
            },

            AccessTokenLifetime = 300, // ,    
            IdentityTokenLifetime = 3600, // ,    

            //    refresh-   scope offline_access
            AllowOfflineAccess = false,
        }
    };
}

, , bob


public static List GetUsers()
{
    return new List
    {
        new TestUser
        {
            SubjectId = "1",
            Username = "alice",
            Password = "password",

            Claims = new List
            {
                new Claim("name", "Alice"),
                new Claim("website", "https://alice.com"),
                new Claim("role", "user"),
            }
        },
        new TestUser
        {
            SubjectId = "2",
            Username = "bob",
            Password = "password",

            Claims = new List
            {
                new Claim("name", "Bob"),
                new Claim("website", "https://bob.com"),
                new Claim("role", "admin"),
            }
        }
    };
}

  1. ConfigureServices

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddIdentityServer(options =>
    {
        // http://docs.identityserver.io/en/release/reference/options.html#refoptions
        options.Endpoints = new EndpointsOptions
        {
            //  Implicit Flow    
            EnableAuthorizeEndpoint = true,
            //    
            EnableCheckSessionEndpoint = true,
            //     
            EnableEndSessionEndpoint = true,
            //   claims   
            // http://openid.net/specs/openid-connect-core-1_0.html#UserInfo
            EnableUserInfoEndpoint = true,
            //  OpenId Connect   
            EnableDiscoveryEndpoint = true,

            //     ,   
            EnableIntrospectionEndpoint = false,
            //    ..  Implicit Flow access_token   authorization_endpoint
            EnableTokenEndpoint = false,
            //    refresh  reference tokens 
            // http://docs.identityserver.io/en/release/topics/reference_tokens.html
            EnableTokenRevocationEndpoint = false
        };

        // IdentitySever  cookie    
        options.Authentication = new IdentityServer4.Configuration.AuthenticationOptions
        {
            CookieLifetime = TimeSpan.FromDays(1)
        };

    })
        //  x509-, IdentityServer  RS256   JWT
        .AddDeveloperSigningCredential()
        //    id_token
        .AddInMemoryIdentityResources(GetIdentityResources())
        //    access_token
        .AddInMemoryApiResources(GetApiResources())
        //   
        .AddInMemoryClients(GetClients())
        //  
        .AddTestUsers(GetUsers());
}

IdentityServer, , , scope OpenId Connect OAuth2.0, -, .


. AddIdentityServer IdentityServer ASP.NET Core, , middleware Configure.


  • IdentityServer RSA SHA 256, - . AddDeveloperSigningCredential JWT-, id_token, access_token . , , .
  • AddInMemoryIdentityResources. , , .

Configure



public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(LogLevel.Debug);
    app.UseDeveloperExceptionPage();

    //  middleware IdentityServer
    app.UseIdentityServer();

    //  2  ,     
    app.UseStaticFiles();
    app.UseMvcWithDefaultRoute();
}

Starter UI IdentityServer, , , wwwroot wwwroot.


, .


2. Api


API .


solution Api, ASP.NET Core 1.1. .. - , -, JSON, MvcCore middleware Mvc.


, Package Manager Console


Install-Package Microsoft.AspNetCore.Mvc.Core -Version 1.1.3
Install-Package Microsoft.AspNetCore.Mvc.Formatters.Json -Version 1.1.3
Install-Package Microsoft.AspNetCore.Cors -Version 1.1.2
Install-Package IdentityServer4.AccessTokenValidation -Version 1.2.1

, Kestrel Program.cs


public static void Main(string[] args)
{
    Console.Title = "API";

    var host = new WebHostBuilder()
        .UseKestrel()
        .UseUrls("http://localhost:5001")
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup()
        .Build();

    host.Run();
}

Startup.cs .
ConfigureServices


public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options=>
    {
        //   CORS,          API
        options.AddPolicy("default", policy =>
        {
            policy.WithOrigins("http://localhost:5003")
                .AllowAnyHeader()
                .AllowAnyMethod();
        });
    });

    //   MVC Core   Razor, DataAnnotations  ,   Asp.NET 4.5 WebApi
    services.AddMvcCore()
        //  ,      Authorize
        .AddAuthorization(options =>
            //      Roles magic strings,     
            options.AddPolicy("AdminsOnly", policyUser =>
            {
                policyUser.RequireClaim("role", "admin");
            })
        )
        //  AddMVC,   AddMvcCore,       JSON 
        .AddJsonFormatters();

}

Configure


public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(LogLevel.Debug);

    //  middleware  CORS 
    app.UseCors("default");

    //  middleware      OpenId  Connect JWT-
    app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
    {
        //  IdentityServer
        Authority = "http://localhost:5000",
        // ,     HTTPS    IdentityServer,   true  
        // https://docs.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.builder.openidconnectoptions
        RequireHttpsMetadata = false,

        //        aud  access_token JWT
        ApiName = "api1",

        //   ,      api   scopes      scope
        // AllowedScopes = { "api1.read", "api1.write" }

        //  JWT-   claims   HttpContext.User      Authorize  ,  
        AutomaticAuthenticate = true,
        //   middleware     authentication challenge
        AutomaticChallenge = true,

        //   [Authorize],  IdentityServerAuthenticationOptions -   
        RoleClaimType = "role", 
    });

    app.UseMvc();
}

, Claims , , , middleware IdentityServer access_token.
IdentityController.
C .


using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;

namespace Api.Controllers
{

    [Authorize]
    public class IdentityController : ControllerBase
    {
        [HttpGet]
        [Route("identity")]
        public IActionResult Get()
        {
            return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
        }

        [HttpGet]
        [Route("superpowers")]
        [Authorize(Policy = "AdminsOnly")]
        public IActionResult Superpowers()
        {
            return new JsonResult("Superpowers!");
        }
    }
}

, .


3. Client


. - Kestrel, .


, 2 , Client.


.


Install-Package Microsoft.AspNetCore.StaticFiles -Version 1.1.2

Program.cs


public static void Main(string[] args)
{
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseUrls("http://localhost:5003")
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup()
        .Build();

    host.Run();
}

Startup .


public void ConfigureServices(IServiceCollection services)
{
}
public void Configure(IApplicationBuilder app)
{
    app.UseDefaultFiles();
    app.UseStaticFiles();
}

JavaScript, , Api.


wwwroot .


  • index.html HTML- JavaScript- app.js oidc-client.js.
  • oidc-client.js , OpenId Connect
  • app.js oidc-client
  • callback.html , , , .
  • callback-silent.html , callback.html, , "" iframe. refresh_token.

index.html
HTML- wwwroot .





    
    


    
    
    
    
    

    


    
    


oidc-client.js
(1.3.0) .


app.js
JavaScript- wwwroot .



/// 

IntelliSense.


app.js


Oidc.Log.logger = console;
Oidc.Log.level = 4;

, , oidc-client. . , , , .


.


, , , . UserManager oidc-client, . .


var config = {

    authority: "http://localhost:5000", //   IdentityServer
    client_id: "js", //      IdentityServer
    //  ,         
    //      -     OpenId Connect
    redirect_uri: "http://localhost:5003/callback.html",
    // Response Type   ,   Authorization Endpoint
    //   ,    Implicit Flow
    // http://openid.net/specs/openid-connect-core-1_0.html#Authentication
    response_type: "id_token token",
    //  subject id ,      id_token,    access_token    api1 (. c IdentityServer)
    scope: "openid profile api1",
    // ,          
    post_logout_redirect_uri: "http://localhost:5003/index.html",
    //      IdentityServer,   true
    monitorSession: true,
    //   ,       ,   2000
    checkSessionInterval: 30000,
    //  access_token     https://tools.ietf.org/html/rfc7009
    revokeAccessTokenOnSignout: true,
    //       ,    ,   300
    // https://github.com/IdentityModel/oidc-client-js/blob/1.3.0/src/JoseUtil.js#L95
    clockSkew: 300,
    //     UserInfo endpoint  ,      
    loadUserInfo: true,
};
var mgr = new Oidc.UserManager(config);

.


function login() {
    //  
    mgr.signinRedirect();
}

function displayUser() {
    mgr.getUser().then(function (user) {
        if (user) {
            log("User logged in", user.profile);
        }
        else {
            log("User not logged in");
        }
    });
}

function api() {
    //   claims 
    requestUrl(mgr, "http://localhost:5001/identity");
}

function getSuperpowers() {
    //  endpoint   
    requestUrl(mgr, "http://localhost:5001/superpowers");
}

function logout() {
    //  
    mgr.signoutRedirect();
}

document.getElementById("login").addEventListener("click", login, false);
document.getElementById("api").addEventListener("click", api, false);
document.getElementById("getSuperpowers").addEventListener("click", getSuperpowers, false);
document.getElementById("logout").addEventListener("click", logout, false);
document.getElementById("getUser").addEventListener("click", displayUser, false);

//      
displayUser();


function requestUrl(mgr, url) {
    mgr.getUser().then(function (user) {
        var xhr = new XMLHttpRequest();
        xhr.open("GET", url);
        xhr.onload = function () {
            log(xhr.status, 200 == xhr.status ? JSON.parse(xhr.responseText) : "An error has occured.");
        }
        //   Authorization  access_token   Bearer - . 
        xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
        xhr.send();
    });
}

function log() {
    document.getElementById('results').innerText = '';

    Array.prototype.forEach.call(arguments, function (msg) {
        if (msg instanceof Error) {
            msg = "Error: " + msg.message;
        }
        else if (typeof msg !== 'string') {
            msg = JSON.stringify(msg, null, 2);
        }
        document.getElementById('results').innerHTML += msg + '\r\n';
    });
}

, , , . wwwroot.


callback.html





    
    


    
    


callback-silent.html





    
    


    
    


!



: , , dotnet run. IdentityServer .


IdentityServer Api, Client.


http://localhost:5003/index.html Client.
clear().


, .
, Chrome 60 .



Network Preserve log .


CTRL+F5.


Happy path


, .
1. , .
2. .
Login.


GET-
http://localhost:5000/.well-known/openid-configuration
oidc-client OpenId Connect ( ), authorization_endpoint
http://localhost:5000/connect/authorize


, WebStorage. oidc-client , , sessionStorage.


authorization_endpoint


client_id js
redirect_uri http://localhost:5003/callback.html
response_type id_token token
scope openid profile api1
state
nonce

, redirect_uri , client_id js IdentityServer.


.. , IdentityServer .


http://localhost:5000/account/login.


3. .
4. .
5. id token' , , access token'.


bob password , .
authorization_endpoint, OpenId Connect relying party ( js-) scopes.


, . , authorization_endpoint, authorization_endpoint cookie.


, redirect_uri .


Implicit Flow #. , JavaScript, -.


id_token
access_token API
token_type access_token, Bearer
expires_in access_token
scope scopes

6. id token Subject Identifier .


oidc-client state, nonce id_token. , (, sub claim id_token). id_token oidc-client .


id_token ( Network ), , payload -


{
  "nbf": 1505143180,
  "exp": 1505146780,
  "iss": "http://localhost:5000",
  "aud": "js",
  "nonce": "2bd3ed0b260e407e8edd0d03a32f150c",
  "iat": 1505143180,
  "at_hash": "UAeZEg7xr23ToH2R2aUGOA",
  "sid": "053b5d83fd8d3ce3b13d3b175d5317f2",
  "sub": "2",
  "auth_time": 1505143180,
  "idp": "local",
  "name": "Bob",
  "website": "https://bob.com",
  "amr": [
    "pwd"
  ]
}

at_hash, .


access_token payload , , .


{
  "nbf": 1505143180,
  "exp": 1505143480,
  "iss": "http://localhost:5000",
  "aud": [
    "http://localhost:5000/resources",
    "api1"
  ],
  "client_id": "js",
  "sub": "2",
  "auth_time": 1505143180,
  "idp": "local",
  "name": "Bob",
  "role": "admin",
  "scope": [
    "openid",
    "profile",
    "api1"
  ],
  "amr": [
    "pwd"
  ]
}

, . , IdentityServer.


, claims id_token .


, loadUserInfo, UserInfo Endpoint. UserInfo Endpoint claims Authorization Bearer- access_token, claims JavaScript- .


loadUserInfo access_token, HTTP-, .


API


"Call API".
ajax- http://localhost:5001/identity.
, OPTIONS- CORS .. , "" (Authorization, ).


, , GET-. , Authorization Bearer < access_token>.


IdentityServer middleware . IdentityServer middleware Asp.Net Core JwtBearerMiddleware.


, 200.


Logout


GET- end_session_endpoint


id_token_hint id_token
post_logout_redirect_uri URI, ,

, .



, . , - , - .


alice Get Superpowers!, bob .



do not allow


Logout ,
Username: alice
Password: password


http://localhost:5000/consent No, Do Not Allow.


http://localhost:5003/callback.html.
, URL #error=access_denied, signinRedirectCallback , rejected.


callback.html catch-, .



id_token URL , claims, scope profile.


Claims, scope profile .


API .


api1


claim api1


"scope": [
    "openid",
    "profile"
],

Api 401 (Unathorized).


access_token


access_token, Call API.


API ! , IdentityServer middleware Asp.Net Core, ClockSkew. , , , , . ClockSkew 5 .


5 , API 401 (Unathorized).


401, access_token.


access_token


app.js config ,


var config = {
    // ...
    //  true,    access_token   ,   false
    automaticSilentRenew: true,
    //     ""     iframe
    silent_redirect_uri: 'http://localhost:5003/callback-silent.html',
    //      oidc-client   access_token
    accessTokenExpiringNotificationTime: 60,
    // ...
} 

access_token. Call API , .


id_token


access_token API , , , id_token . js-. .



, :


  1. OpenId Connect Implicit Flow IdentityServer oidc-client ASP.NET Core 1.1.
  2. , .
  3. , , , , , .


  1. .
  2. IdentityServer4
  3. oidc-client.
  4. ASP.NET Core. .
  5. Authorize IdentityServer.
  6. OpenId Connect 2 id_token access_token .
  7. OpenId Connect ASP.NET Core.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/337784/

:  

: [1] []
 

:
: 

: ( )

:

  URL