2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWS日記24 (Cognito - OIDC)

Last updated at Posted at 2021-02-28

はじめに

今回は AWS Cognito の OpenID Connect (OIDC) を試します。
OIDCを利用しGitHubとアカウント連携します。
GitHub OpenID Connect Wrapper for Cognitoを参考に進めます。

Lambda関数・SAMテンプレート

準備

AWS SAM の準備をします

[AWS Cognitoの資料]
AWS Cognito
ユーザープールへの OIDC ID プロバイダーの追加

AWS SAM テンプレート作成

AWS SAM テンプレートで API-Gateway , Lambda, AWS Cognito の設定をします。

[参考資料]
AWS SAM テンプレートを作成する

AWS Cognito設定用のSAMテンプレート

AWS Cognito の設定は以下の部分

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Serverless Account Page

Parameters:
  ApplicationName:
    Type: String
    Default: 'ServerlessAccountPage'
  UserPoolClientName:
    Type: String
    Default: 'GitHubOpenIdAuthClient'
  UserPoolDefaultDomainPrefixName:
    Type: String
    Default: 'test-user-pool-domain2021'
  UserPoolClientCallbackURL:
    Type: String
    Default: ''

Resources:
  UserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: false
        UnusedAccountValidityDays: 7
      AutoVerifiedAttributes:
        - 'email'
      MfaConfiguration: 'OFF'
  UserPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      UserPoolId: !Ref UserPool
      ClientName: !Ref UserPoolClientName
      AllowedOAuthFlows:
        - 'implicit'
      AllowedOAuthFlowsUserPoolClient: true
      AllowedOAuthScopes:
        - 'openid'
      ExplicitAuthFlows:
        - 'ALLOW_REFRESH_TOKEN_AUTH'
      GenerateSecret: false
      CallbackURLs:
        - !Ref UserPoolClientCallbackURL
  UserPoolDomain:
    Type: AWS::Cognito::UserPoolDomain
    Properties:
      UserPoolId: !Ref UserPool
      Domain: !Ref UserPoolDefaultDomainPrefixName

Outputs:
  APIURI:
    Description: "Amazon Cognito Domain"
    Value: !Join [ '', [ 'https://', !Ref UserPoolDomain, '.auth.', !Ref 'AWS::Region', '.amazoncognito.com'] ]

GitHub OAuth App ラッパー用のSAMテンプレート

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Serverless Cognito GitHub OpenId Page

Globals:
  Function:
    Runtime: go1.x
    Timeout: 15
    Environment:
      Variables:
        REGION: !Ref 'AWS::Region'
        ALGORITHM: !Ref Algorithm
        KEY_ID: !Ref KeyId
        CERT_PATH: !Ref CertPath
        PUB_KEY_PATH: !Ref PubKeyPath
        GITHUB_CLIENT_ID: !Ref GitHubClientId
        GITHUB_CLIENT_SECRET: !Ref GitHubClientSecret
        COGNITO_REDIRECT_URI: !Ref CognitoRedirectUri
        GITHUB_API_URL: !Ref GitHubUrl
        GITHUB_LOGIN_URL: !Ref GitHubLoginUrl

Parameters:
  GitHubClientId:
    Type: String
  GitHubClientSecret:
    Type: String
  CognitoRedirectUri:
    Type: String
  GitHubUrl:
    Type: String
    Default: "https://api.github.com"
    MinLength: 1
  GitHubLoginUrl:
    Type: String
    Default: "https://github.com/login"
    MinLength: 1
  StageName:
    Type: String
    Default: "CognitoGitHubApiStage"
  CertPath:
    Type: String
    Default: "jwtRS256.key"
  PubKeyPath:
    Type: String
    Default: "jwtRS256.key.pub"
  Algorithm:
    Type: String
    Default: "RS256"
  KeyId:
    Type: String
    Default: "jwtRS256"

Resources:
  GithubOAuthApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Ref StageName
      OpenApiVersion: "2.0"
  OpenIdDiscovery:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: api/discovery/bin/
      Handler: main
      MemorySize: 256
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /.well-known/openid-configuration
            Method: get
            RestApiId: !Ref GithubOAuthApi
  Authorize:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: api/authorize/bin/
      Handler: main
      MemorySize: 256
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /authorize
            Method: get
            RestApiId: !Ref GithubOAuthApi
  Token:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: api/token/bin/
      Handler: main
      MemorySize: 256
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /token
            Method: get
            RestApiId: !Ref GithubOAuthApi
        PostResource:
          Type: Api
          Properties:
            Path: /token
            Method: post
            RestApiId: !Ref GithubOAuthApi
  UserInfo:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: api/userinfo/bin/
      Handler: main
      MemorySize: 256
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /userinfo
            Method: get
            RestApiId: !Ref GithubOAuthApi
        PostResource:
          Type: Api
          Properties:
            Path: /userinfo
            Method: post
            RestApiId: !Ref GithubOAuthApi
  Jwks:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: api/jwks/bin/
      Handler: main
      MemorySize: 256
      Events:
        GetResource:
          Type: Api
          Properties:
            Path: /.well-known/jwks.json
            Method: get
            RestApiId: !Ref GithubOAuthApi

Outputs:
    CognitoGitHub:
      Description: "Cognito GitHub Api URL"
      Value: !Sub "https://${GithubOAuthApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}"

フロントエンド用のSAMテンプレート

AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: Serverless Cognito OpenId FrontPage

Parameters:
  ApplicationName:
    Type: String
    Default: 'ServerlessCognitoOpenIdFrontPage'
  CognitoUrl:
    Type: String
    Default: 'https://your-domain-prefix.auth.your-region.amazoncognito.com'
  CognitoClientId:
    Type: String
    Default: 'abc012'

Resources:
  FrontPageApi:
    Type: AWS::Serverless::HttpApi
  FrontPageFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: ServerlessCognitoOpenIdFrontFunction
      CodeUri: bin/
      Handler: main
      MemorySize: 256
      Runtime: go1.x
      Description: 'Cognito OpenId Front Function'
      Environment:
        Variables:
          REGION: !Ref 'AWS::Region'
          COGNITO_URL: !Ref CognitoUrl
          COGNITO_CLIENT_ID: !Ref CognitoClientId
      Events:
        FrontPageApi:
          Type: HttpApi
          Properties:
            Path: '/'
            Method: get
            ApiId: !Ref FrontPageApi
        FrontProxyApi:
          Type: HttpApi
          Properties:
            Path: '/{proxy+}'
            Method: get
            ApiId: !Ref FrontPageApi

Outputs:
  APIURI:
    Description: "URI"
    Value: !Join [ '', [ 'https://', !Ref FrontPageApi, '.execute-api.',!Ref 'AWS::Region','.amazonaws.com/'] ]

Lambda関数作成

※ JWT周りの処理は github.com/dgrijalva/jwt-go を利用しました。

func getJwks()(JSONWebKey, error) {
	res := JSONWebKey{}
	res.Algorithm = os.Getenv("ALGORITHM")
	res.KeyId = os.Getenv("KEY_ID")
	res.KeyType = "RSA"
	res.PublicKeyUse = "sig"
	key, err := os.ReadFile(os.Getenv("PUB_KEY_PATH"))
	if err != nil {
		return res, err
	}
	verifyKey, err := jwt.ParseRSAPublicKeyFromPEM(key)
	if err != nil {
		return res, err
	}
	res.RSAModulus = joseBase64UrlEncode(verifyKey.N.Bytes())
	res.RSAExponent = joseBase64UrlEncode(serializeRSAPublicExponentParam(verifyKey.E))
	return res, nil
}

func joseBase64UrlEncode(b []byte) string {
	return strings.TrimRight(base64.URLEncoding.EncodeToString(b), "=")
}

func serializeRSAPublicExponentParam(e int) []byte {
	buf := make([]byte, 4)
	binary.BigEndian.PutUint32(buf, uint32(e))
	var i int
	for i = 0; i < 8; i++ {
		if buf[i] != 0 {
			break
		}
	}
	return buf[i:]
}

終わりに

外部サービス(GitHub)の設定を行う必要があるため、SAMテンプレートを複数に分ける必要がありました。
GitHubだけでなく他の外部サービスにも応用ができるため、今後試そうと思います。
今回JWTに触れることになり、とても勉強になりました。

参考資料
2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?