composeパッケージの役割
composeパッケージ(compose/compose.go
)はフレームワークユーザーがOAuth2Provider
インターフェースを通してアクセスできる機能を決定するためのパッケージです。
なぜConfigのメンバーのハンドラーはスライスなのか
Compose関数とComposeAllEnable関数
type Factory func(config fosite.Configurator, storage interface{}, strategy interface{}) interface{}
func Compose(config *fosite.Config, storage interface{}, strategy interface{}, factories ...Factory) fosite.OAuth2Provider {
f := fosite.NewOAuth2Provider(storage.(fosite.Storage), config)
for _, factory := range factories {
res := factory(config, storage, strategy)
if ah, ok := res.(fosite.AuthorizeEndpointHandler); ok {
config.AuthorizeEndpointHandlers.Append(ah)
}
if th, ok := res.(fosite.TokenEndpointHandler); ok {
config.TokenEndpointHandlers.Append(th)
}
if tv, ok := res.(fosite.TokenIntrospector); ok {
config.TokenIntrospectionHandlers.Append(tv)
}
if rh, ok := res.(fosite.RevocationHandler); ok {
config.RevocationHandlers.Append(rh)
}
if ph, ok := res.(fosite.PushedAuthorizeEndpointHandler); ok {
config.PushedAuthorizeEndpointHandlers.Append(ph)
}
}
return f
}
func ComposeAllEnabled(config *fosite.Config, storage interface{}, key interface{}) fosite.OAuth2Provider {
keyGetter := func(context.Context) (interface{}, error) {
return key, nil
}
return Compose(
config,
storage,
&CommonStrategy{
CoreStrategy: NewOAuth2HMACStrategy(config),
OpenIDConnectTokenStrategy: NewOpenIDConnectStrategy(keyGetter, config),
Signer: &jwt.DefaultSigner{GetPrivateKey: keyGetter},
},
OAuth2AuthorizeExplicitFactory,
OAuth2AuthorizeImplicitFactory,
OAuth2ClientCredentialsGrantFactory,
OAuth2RefreshTokenGrantFactory,
OAuth2ResourceOwnerPasswordCredentialsFactory,
RFC7523AssertionGrantFactory,
OpenIDConnectExplicitFactory,
OpenIDConnectImplicitFactory,
OpenIDConnectHybridFactory,
OpenIDConnectRefreshFactory,
OAuth2TokenIntrospectionFactory,
OAuth2TokenRevocationFactory,
OAuth2PKCEFactory,
PushedAuthorizeHandlerFactory,
)
}
Compose関数で可変長引数(variadic)のfactoriesに渡されたFactory
からhandler.go
やintrospect.go
に定義されたハンドラーの具象型を生成し、Config
型のメンバーのハンドラーに機能を搭載していきます。
なぜ各エンドポイントのハンドラーはスライスになっているのでしょうか。
/authorize
、/token
、/introspect
、/revoke
などといったHTTPエンドポイントは一つしか定義できないじゃないか、と思ったりもするかもしれません。
これは各HTTPエンドポイントで様々な仕様の認可リクエストや認可レスポンス、トークンリクエストやトークンレスポンスに対応する必要があるためだから、と思われます。
OAuth2だけでも/authorize
エンドポイントは様々なresponse_type
に対応する必要があり、さらにはOIDC対応するとなると条件分岐は複雑になります。
そのためStrategyパターンを用いて条件に合致するハンドラーで処理するという方式を取っています。
Strategyはどこで使われているか
Config
型のメンバーとなっているハンドラーinterface(のスライス)には先述の通りCompose関数で具象型(strategy)が追加されます。
ではその具象型はどこから使われているかというと、それぞれ以下の通りです。
各ハンドラーに追加された具象型であるstrategyを使ってリクエスト・レスポンスを処理していきます。
パラメータを渡してそれぞれのstrategyは処理をし、
- そのstrategyが処理担当で正常処理の場合はそのままloop継続します
- パラメータは参照渡しになっており、処理結果等はそこに書かれます
- そのstrategyが処理担当で処理エラーの場合はエラーを返します
- そのstrategyが処理担当外の場合はnilを返します
AuthorizeEndpointHandler
authorize_response_writer.go
の42行目
for _, h := range f.Config.GetAuthorizeEndpointHandlers(ctx) {
if err := h.HandleAuthorizeEndpointRequest(ctx, ar, resp); err != nil {
return nil, err
}
}
TokenEndpointHandler
access_response_writer.go
の41行目
for _, tk = range f.Config.GetTokenEndpointHandlers(ctx) {
if err = tk.PopulateTokenEndpointResponse(ctx, requester, response); err == nil {
// do nothing
} else if errors.Is(err, ErrUnknownRequest) {
// do nothing
} else if err != nil {
return nil, err
}
}
RevocationHandler
revoke_handler.go
の72行目
for _, loader := range f.Config.GetRevocationHandlers(ctx) {
if err := loader.RevokeToken(ctx, token, tokenTypeHint, client); err == nil {
found = true
} else if errors.Is(err, ErrUnknownRequest) {
// do nothing
} else if err != nil {
return err
}
}
PushedAuthorizeEndpointHandler
pushed_authorize_response_wirter.go
の29行目
for _, h := range handlersProvider.GetPushedAuthorizeEndpointHandlers(ctx) {
if err := h.HandlePushedAuthorizeEndpointRequest(ctx, ar, resp); err != nil {
return nil, err
}
}
TokenIntrospector
introspect.go
の62行目
for _, validator := range f.Config.GetTokenIntrospectionHandlers(ctx) {
tu, err := validator.IntrospectToken(ctx, token, tokenUse, ar, scopes)
if err == nil {
found = true
foundTokenUse = tu
} else if errors.Is(err, ErrUnknownRequest) {
// do nothing
} else {
rfcerr := ErrorToRFC6749Error(err)
return "", nil, errorsx.WithStack(rfcerr)
}
}
Strategyのロジックはどこにあるのか
handlerディレクトリ以下に存在します。
% tree ./handler -L 1
./handler
├── oauth2
├── openid
├── par
├── pkce
└── rfc7523