Cognito 를 이용한 인증처리 - 02 (Backend 에서 Token 처리)

2023. 7. 17. 00:01ASPNET/AWS

반응형

이전 강좌에서 사용한 SignalR Template 을 활용하겠다. 

https://yogingang.tistory.com/440

 

ASPNET Core SignalR 을 이용한 Push Server 구현 - 3 (vertical slice architecture)

https://yogingang.tistory.com/437 ASPNET Core SignalR 을 이용한 Push Server 구현 - 1 signalR core 는 dotnet core 에서 실시간 통신을 위해 내놓은 비동기 양방향 통신 frameworks 이다. aspnet core 위에서 돌아가는 형태 이

yogingang.tistory.com

 

aspnet core 에서는 cognito 로 부터 받은 accesstoken 을 validation 하는 방법은 여러가지 있는데

간단하게 확인 하는 두가지 방법이 있다. 

1. aspnet core 용 aws cognito package 이용

다음 두 package 를 설치하고 진행한다. 

dotnet add package Amazon.AspNetCore.Identity.Cognito
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

Program.cs

//builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthentication().AddJwtBearer(options =>
{
    options.Authority = builder.Configuration.GetSection("Cognito").GetValue<string>("Authority");
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        ValidateAudience = false,
        ValidateIssuer = false,
    };
    options.Events = new JwtBearerEvents
    {
        OnMessageReceived = context =>
        {
            var accessToken = context.Request.Query["access_token"];
            
            // If the request is for our hub...
            var path = context.HttpContext.Request.Path;
            if (!string.IsNullOrEmpty(accessToken) &&
                (path.StartsWithSegments("/channel")))
            {
                // Read the token out of the query string
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});
"Cognito": {
    "Authority": "https://cognito-idp.{region}.amazonaws.com/{userpoolid}"
  },

 

ChannelHub.cs

//[Authorize("ChannelHubAuthorizationPolicy")]
[Authorize]
public class ChannelHub : Hub

기본 Authorize 를 사용하도록 수정한다. 

이제 signalR server 를 실행하자

이제 이전 강좌에서 사용한 aws cli 를 이용해 cognito 에 login 하여 access token 을 얻어오자

aws cognito-idp initiate-auth --region YOU_REGION --auth-flow USER_PASSWORD_AUTH --client-id YOUR_CLIENT_ID --auth-parameters USERNAME=YOUR_EMAIL,PASSWORD=YOUR_PASSWORD

해당 token 을 이용하여 signalR server 에 접속해 보면 정상 처리되는 걸 확인 할 수 있다.

 

 

 

2. aspnet core 에서 httpclient 를 이용하여 직접 parsing 하여 처리

cognito 관련 package 없이 처리 (일반적인 openid connect 관련 service 들 == google, azure 등 은 이 방법을 써야 한다. )

 

dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

 

jwt token 에서 issuer 를 가져와서

issuer/.well-known/openid-configuration 통해 jwkUri 를 가져와서 

해당 jwkUri 로 접근하여 (아마도 json data) key 들을 가져오면 된다. 

그리고 그것을 처리 하자

program.cs

builder.Services.AddHttpClient();
....
....
// jwt token validataion 시에 오류 정보를 자세히 보기위해서 다음을 추가하자
IdentityModelEventSource.ShowPII = true;

ChannelHubAuthorizationHandler.cs

 

using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens;
using SignalRTemplate.Extensions;
using SignalRTemplate.Shared.Interfaces;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;

namespace SignalRTemplate.Authorization;

public class ChannelHubAuthorizationRequirement : IAuthorizationRequirement
{
}

public class ChannelHubAuthorizationHandler : AuthorizationHandler<ChannelHubAuthorizationRequirement>
{

    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IConfiguration _configuration;
    private readonly ILogger _logger;
    private readonly HttpClient _httpClient;

    public ChannelHubAuthorizationHandler(IHttpContextAccessor httpContextAccessor
        , IConfiguration configuration
        , ILogger<ChannelHubAuthorizationHandler> logger
        , IHttpClientFactory httpClientFactory)
    {
        _httpContextAccessor = httpContextAccessor;
        _configuration = configuration;
        _logger = logger;
        _httpClient = httpClientFactory.CreateClient();

    }

    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ChannelHubAuthorizationRequirement requirement)
    {
        var request = _httpContextAccessor.HttpContext.Request;


        string jwtToken = string.Empty;
        var token = request?.Headers["Authorization"];
        AuthenticationHeaderValue.TryParse(token, out var tokenValue);
        jwtToken = tokenValue?.Parameter ?? "";

        jwtToken = jwtToken == "" ? request?.Query["access_token"].FirstOrDefault() ?? string.Empty : jwtToken;

        if (ValidateToken(jwtToken))
        {
            _logger.LogDebug("Signalr authorization succeed");
            context.Succeed(requirement);
        }
        else
        {
            _logger.LogDebug("Signalr authorization failed");
            context.Fail();
        }

    }


    private bool ValidateToken(string token)
    {
        try
        {
            var handler = new JwtSecurityTokenHandler();
            var validationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false,
                ValidateAudience = false,
                ValidateIssuerSigningKey = true,
                CryptoProviderFactory = new CryptoProviderFactory
                {
                    CacheSignatureProviders = false
                },
                IssuerSigningKeyResolver = (token, securityToken, kid, parameters) =>
                {
                    // openid configuration 정보 가져오기
                    var url = $"{securityToken.Issuer}/.well-known/openid-configuration";
                    var configData = GetData(url);
                    var openIdConfig = configData.DeserializeFromJson<OpenIdConfiguration>();

                    // JwksUri 로 부터 jsonwebkey list 가져오기
                    var jwksData = GetData(openIdConfig?.JwksUri);
                    var jsonWebKeySet = new JsonWebKeySet(jwksData);

                    return jsonWebKeySet.Keys;
                },
            };

            var principal = handler.ValidateToken(token, validationParameters, out var jwtToken);
            
            return true;

        }
        catch (Exception e)
        {
            _logger.LogInformation(e.Message);
            return false;
        }
    }

    private string GetData(string? url)
    {
        if (url == null) return string.Empty;


        var request = new HttpRequestMessage(HttpMethod.Get, url);
        var response = _httpClient.Send(request);
        response.EnsureSuccessStatusCode();
        using var stream = response.Content.ReadAsStream();
        using var streamReader = new StreamReader(stream);
        return streamReader.ReadToEnd();
    }
}

 

이제 이전 강좌에서 사용한 aws cli 를 이용해 cognito 에 login 하여 access token 을 얻어오자

aws cognito-idp initiate-auth --region YOU_REGION --auth-flow USER_PASSWORD_AUTH --client-id YOUR_CLIENT_ID --auth-parameters USERNAME=YOUR_EMAIL,PASSWORD=YOUR_PASSWORD

해당 token 을 이용하여 signalR server 에 접속해 보면 정상 처리되는 걸 확인 할 수 있다.

 

 

 

관련영상

https://youtu.be/A-nvnou40Hc

 

반응형