3
Help us understand the problem. What are the problem?

posted at

updated at

GolangでCognito認証をしてトークンをjwt形式にする

できること

認証はuserPool、認可はIdentitiyPoolで行われ、それぞれ別のリソース

認証

  • Cognitoで管理されているユーザー情報で認証ができる
  • 外部プロバイダー(Azure、Google、Facebook)と連携が可能
  • ユーザー認証が成功すると、トークンが発行され、アプリケーション側でトークンの検証を行うことで認証機能をつくることができる
  • UserPoolで行われる

認可

  • 一時的な権限を与える
  • チケットを渡すイメージ
  • Cognitoではグループや属性ごとにIAMRoleを割り振ったりすることができる
  • IndentityPoolで行われる

Cognito設定

  • ALLOW_ADMIN_USER_PASSWORD_AUTHにチェックが必要
  • AppClient作成時に、ClientSecretを使うのチェックを外す
    image.png

サンプルコード

main.go
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"os"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/cognitoidentityprovider"
	"github.com/golang-jwt/jwt/v4"
)

type Tokens struct {
	Id      string
	Access  string
	Refresh string
}

func main() {
	tokens := cognitoAuth()
	inspectionjwt(tokens.Id)
}

func inspectionjwt(tokenString string) {

	CustomClaims := jwt.MapClaims{}

	// tokenからjwt形式へ変換する
	token, _ := jwt.ParseWithClaims(tokenString, CustomClaims, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.New("unexpected signing method")
		}
		return []byte("test"), nil
	})

	// 一度JSONへ変換
	jsonString, err := json.Marshal(token.Claims)
	if err != nil {
		fmt.Println("JSON Marshal error: ", err)
	}

	// Cognitoから返されるTokenの構造に合わせてStructを定義する
	type JwtClaims struct {
		Email string `json:"email"`
		User  string `json:"cognito:username"`
	}

	var jwtClaims JwtClaims

	// 定義したStructへ変換
	if err := json.Unmarshal(jsonString, &jwtClaims); err != nil {
		fmt.Println("jsonunmarshal error")
	}

	// メールアドレス、名前を表示
	fmt.Println(jwtClaims.Email)
	fmt.Println(jwtClaims.User)
}

func cognitoAuth() Tokens {
	if len(os.Args) != 3 {
		log.Fatal("invalid args")
	}
	name := os.Args[1]
	password := os.Args[2]

	params := &cognitoidentityprovider.AdminInitiateAuthInput{
		AuthFlow: aws.String(cognitoidentityprovider.AuthFlowTypeAdminNoSrpAuth),
		AuthParameters: map[string]*string{
			"USERNAME": aws.String(name),
			"PASSWORD": aws.String(password),
		},
		ClientId:   aws.String(os.Getenv("CLIENT_ID")),
		UserPoolId: aws.String(os.Getenv("USER_POOL_ID")),
	}

	client := cognitoidentityprovider.New(
		session.Must(session.NewSessionWithOptions(session.Options{
			SharedConfigState: session.SharedConfigEnable,
		})),
	)

	res, err := client.AdminInitiateAuth(params)
	if err != nil {
		log.Print("AdminAuth Error")
		log.Fatal(err)
	}
	if res == nil || res.AuthenticationResult == nil || res.AuthenticationResult.IdToken == nil {
		log.Fatal("failed to login")
	}

	var tokens Tokens

	tokens.Id = *res.AuthenticationResult.IdToken
	tokens.Access = *res.AuthenticationResult.AccessToken
	tokens.Refresh = *res.AuthenticationResult.RefreshToken

	return tokens
}

実行

go run main.go yuta password

出力

yuta@vamdemicsystem.black
kujirai

トークン

IDトークン、Accessトークン、リフレッシュトークンの3つが発行される

IDトークン

  • 認証されたユーザーの情報がJWT形式で含まれている
  • グループや属性などを拾うことができる
  • デフォルト1時間でExpireする
  • アプリケーションで認証機能を作る場合は、基本的にこれを使う

アクセストークン

リフレッシュートークン

  • IDトークンとアクセストークンを再発行できるトークン
  • 数時間~数日単位で期限を設定することができる
  • トークンが切れるとユーザーは再認証を求められることになるが、リフレッシュートークンを利用するとこでユーザーに操作させることなく、トークンを再発行させることができる

参考

https://tech.anti-pattern.co.jp/golang-cognito/
https://dev.classmethod.jp/articles/study-tokens-of-cognito-user-pools/
https://zenn.dev/takamin55/articles/8442a7583fe5fa

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
3
Help us understand the problem. What are the problem?