개발이야기/AspNet&C#

IdentityServer 학습 #6 - OpenID Connect

Roslyn 2024. 3. 7. 14:33
반응형

이제부터는 OpenID Connect를 학습해 봅시다.

 

(1) 먼저 OpenID Connect를 위한 UI를 IdentityServer에 추가해 줍니다.

dotnet new isui

 

(2) 주석 처리되어 있던 Razor 관련 항목들을 활성화 시켜 줍니다.

using Serilog;

namespace IdentityServer;

internal static class HostingExtensions
{
    public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
    {
        // uncomment if you want to add a UI
        builder.Services.AddRazorPages();
		builder.Services.AddIdentityServer(options =>
            {
                // https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/api_scopes#authorization-based-on-scopes
                options.EmitStaticAudienceClaim = true;
            })
            .AddInMemoryIdentityResources(Config.IdentityResources)
            .AddInMemoryApiScopes(Config.ApiScopes)
            .AddInMemoryClients(Config.Clients)
            .AddTestUsers(TestUsers.Users)
            ;

		return builder.Build();
    }
    
    public static WebApplication ConfigurePipeline(this WebApplication app)
    { 
        app.UseSerilogRequestLogging();
    
        if (app.Environment.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        // uncomment if you want to add a UI
        app.UseStaticFiles();
        app.UseRouting();

        app.UseIdentityServer();

        // uncomment if you want to add a UI
        app.UseAuthorization();
        app.MapRazorPages().RequireAuthorization();

        return app;
    }
}

 

(3) Config.cs 파일의 내용을 아래와 같이 수정합니다.

using Duende.IdentityServer;
using Duende.IdentityServer.Models;

namespace IdentityServer;

public static class Config
{
    public static IEnumerable<IdentityResource> IdentityResources =>
        new IdentityResource[]
        { 
            new IdentityResources.OpenId(),
            new IdentityResources.Profile()
        };

    public static IEnumerable<ApiScope> ApiScopes =>
        new ApiScope[]
        {
			new ApiScope(name: "api1", displayName: "My API")
		};

public static IEnumerable<Client> Clients =>
    new List<Client>
    {
        // machine to machine client (from quickstart 1)
        new Client
        {
            ClientId = "client",
            ClientSecrets = { new Secret("secret".Sha256()) },

            AllowedGrantTypes = GrantTypes.ClientCredentials,
            // scopes that client has access to
            AllowedScopes = { "api1" }
        },
        // interactive ASP.NET Core Web App
        new Client
        {
            ClientId = "web",
            ClientSecrets = { new Secret("secret".Sha256()) },

            AllowedGrantTypes = GrantTypes.Code,
            
            // where to redirect to after login
            RedirectUris = { "https://localhost:5002/signin-oidc" },

            // where to redirect to after logout
            PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },

            AllowedScopes =
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile
            }
        }
    };
}

 

이제부터는 웹앱을 추가하여 테스트를 진행합니다.

 

(1) 새로운 웹앱 프로젝트를 추가해 줍니다.

dotnet new webapp -n {프로젝트명}

 

(2) 해당 프로젝트를 솔루션에 포함시켜 줍니다.

dotnet sln add ./{프로젝트폴더}

 

(3) Microsoft.AspNetCore.Authentication.OpenIdConnect 라이브러리를 Nuget을 통해 설치해 줍니다.

dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect

 

(4) 추가한 웹앱의 Program.cs 파일에 다음 내용을 추가해 줍니다.

builder.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {
        options.Authority = "https://localhost:5001";

        options.ClientId = "web";
        options.ClientSecret = "secret";
        options.ResponseType = "code";

        options.Scope.Clear();
        options.Scope.Add("openid");
        options.Scope.Add("profile");

        options.MapInboundClaims = false; // Don't rename claim types

        options.SaveTokens = true;
    });

 

(5) app.MapRazorPages() 함수에 권한 체크를 포함합니다.

app.MapRazorPages().RequireAuthorization();

 

(6) 인증된 쿠키를 표시할 수 있도록 index.cshtml의 내용을 다음과 같이 변경합니다.

@page
@model IndexModel

@using Microsoft.AspNetCore.Authentication

<h2>Claims</h2>

<dl>
    @foreach (var claim in User.Claims)
    {
        <dt>@claim.Type</dt>
        <dd>@claim.Value</dd>
    }
</dl>

<h2>Properties</h2>

<dl>
    @foreach (var prop in (await HttpContext.AuthenticateAsync()).Properties!.Items)
    {
        <dt>@prop.Key</dt>
        <dd>@prop.Value</dd>
    }
</dl>

 

(7) 이제 실행하면, 로그인을 요구하는 화면이 출력됩니다.

 

(8) 테스트 계정인 alice로 로그인 합니다. (아이디 비번 동일)

그럼 로그인 성공과 함께 발행된 토큰 내역이 나옵니다.

 

 

 

(9) 마지막으로 로그아웃 페이지를 다음과 같이 추가해 줍니다.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace WebClient.Pages;
public class SignoutModel : PageModel
{
    public IActionResult OnGet()
    {
        return SignOut("Cookies", "oidc");
    }
}

 

(10) 해당 경로로 로그아웃 링크를 걸면 로그아웃 처리 페이지로 이동됩니다.

<a class="nav-link text-dark" asp-area="" asp-page="/Signout">Signout</a>

 

 

반응형