Skip to content

Instantly share code, notes, and snippets.

@yukpiz
Last active November 9, 2019 08:22
Show Gist options
  • Save yukpiz/e2804f182067d5318fa94a2d6f868ffb to your computer and use it in GitHub Desktop.
Save yukpiz/e2804f182067d5318fa94a2d6f868ffb to your computer and use it in GitHub Desktop.
package application
import (
"context"
"strings"
)
// ... interfaceやstructの定義は一般的なものなので省略
func (app *application) Authorize(ctx context.Context, token string) (*model.Certification, error) {
// JWTの検証をclientに任せる
clms, err := app.JWTClient.ParseJWT(ctx, token)
// claimsからsubを取得する
var sub string = (*clms)["sub"].(string)
// JWT発行はAuth0なのでsubが空はありえないが、
// 生成は可能なのでgormに空文字を渡さないようにここでブロックする
if len(strings.TrimSpace(sub)) == 0 {
return nil, domain.Errors.NotAuthorizedError
}
// subからユーザー情報を取得する
c, err := app.CertificationRepository.FindByClaimSub(ctx, app.Repository, sub)
if err != nil {
return nil, err
}
if c == nil {
return nil, domain.Errors.NotAuthorizedError
}
return c, nil
}
package client
import (
"context"
"github.com/dgrijalva/jwt-go"
)
// ... interfaceやstructの実装は一般的なものなので省略する
func (c *client) ParseJWT(ctx context.Context, token string) (map[string]interface{}, error) {
// clientの生成時に読み込んだpemファイルからRSA公開鍵を取得する
verifyKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(c.pem))
if err != nil {
return nil, err
}
// JWTの検証とパースを行う
token, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
// トークン認証用のメソッドが正しいかを確認
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
return nil, domain.Errors.JWTNotSupportedMethodError // unexpected signing error
}
return verifyKey, nil
})
if err != nil {
// エラー時のエラー内容を確認して分岐させる
ve, ok := err.(*jwt.ValidationError)
if !ok {
return nil, domain.Errors.JWTValidationError // token is invalided
}
if ve.Errors&jwt.ValidationErrorExpired != 0 {
return nil, domain.Errors.JWTExpiredError // token is expired
}
return nil, err
}
if token == nil || !token.Valid {
return nil, domain.Errors.JWTInvalidError // token is invalided
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, domain.Errors.JWTNotFoundClaimsError // not found claims
}
return &claims, nil
}
package middleware
import (
"context"
"fmt"
"os"
"regexp"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
// ... interfaceやstructの定義は一般的なものなので省略
// grpc_middlewareで埋め込まれるinterceptorの実装になります
// middlewareはリクエストやレスポンスの構造に関与する役割を持っています
func (m *authMiddleware) AuthorizationUnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, uhandler grpc.UnaryHandler) (interface{}, error) {
// TODO: 認証が不要なAPIなどがあれば、
// ここでメソッド名でのマッチをして認証処理をスキップさせる
var token string
var err error
// context.Contextからmetadataを生成します
// gRPCヘッダーを参照できるようになります
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, m.newErrorResponse(nil, xerrors.Errorf("Incoming context error: %v", ctx))
}
// metadataのAuthorizationヘッダからJWTを取り出します
token, err = grpc_auth.AuthFromMD(ctx, MetaDataAuthPrefix)
if err != nil {
return nil, m.newErrorResponse(ctx, err)
}
// JWTの検証とユーザー情報の抽出をapplicationに任せます
c, err := m.AuthApplication.Authorize(ctx, token)
if err != nil {
return nil, m.newErrorResponse(ctx, err)
}
// ユーザー情報をcontextに埋め込みます
// このmiddlewareの後に続くAPIは、contextからユーザー情報を得ることができます
newCtx := context.WithValue(ctx, domain.CertificationKeyName, c)
return uhandler(newCtx, req)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment