Documentation
¶
Overview ¶
Package token は Access Token / Refresh Token の発行・交換・Rotation と、 それらをまとめる Token Family を扱う。
Token Family は Code 交換 1 回ごとに作られ、Refresh Token の Rotation はその中で連鎖する。 Rotation 済みの Refresh Token が再提示されたら Family 全体を失効させる (RFC 9700 §4.14.2)。Code が再利用されたら、その Code から作られた Family を失効させる (RFC 6749 §4.1.2)。
Access Token は既定で Opaque(ADR-0006 §1)。平文は発行時にのみ返し、保存は SHA-256 の Hash のみ。 認可要求の resource(RFC 8707)は Family の Audience として引き継ぎ、トークン要求の resource で その部分集合に絞った Audience を Access Token に付ける(ADR-0008 §3)。
発行は Issuer(issuer.go)が担う。形式は Audience の resource 設定で決まり、Opaque(OpaqueIssuer)か RFC 9068 の JWT(JWTIssuer、jwt.go)。JWT でも Store に行を持ち、Introspection と Revocation は同じ経路。
Refresh Token は Family 作成からの絶対期限(既定 30 日)と最終使用からのスライド期限 (既定 14 日)を持つ。Rotation のたびに後継のスライド期限は延びるが絶対期限は超えない。
ファイル構成: service.go(Exchange の入口と authorization_code)、refresh.go(refresh_token)、 clientcredentials.go(client_credentials)、clientauth.go(クライアント認証)、issuer.go / jwt.go (Access Token の発行)、introspect.go / revoke.go(Introspection と Revocation)。
対応する仕様:
- RFC 6749 §2.3 Client Authentication、§4.1.3 Access Token Request、 §4.1.4 Access Token Response、§4.4 Client Credentials Grant、§5.1 Successful Response、 §5.2 Error Response、§6 Refreshing an Access Token
- RFC 6750 §4 Bearer Token の返却形式
- RFC 7636 §4.5 code_verifier の送信、§4.6 サーバー側の検証
- RFC 8707 §2.2 トークン要求の resource パラメータ(認可済みの範囲内で Audience を絞る)
- RFC 7662 §2.2 Introspection 応答の aud
- RFC 9068 §2 JWT Access Token(typ: at+jwt、§2.2 の Claims、§3 aud は resource indicator)
- RFC 9700 §4.14 Refresh Token Protection(Rotation と再利用検知)
- OIDC Core §12 Using Refresh Tokens(Refresh 応答の ID Token)
Index ¶
- Constants
- Variables
- type AccessStatus
- type AccessToken
- type ByFormat
- type Credentials
- type Error
- type Family
- type FamilyStatus
- type IDTokenInput
- type IDTokenIssuer
- type Introspection
- type IntrospectionRequest
- type IssueInput
- type Issuer
- type JWTIssuer
- type OpaqueIssuer
- type RefreshStatus
- type RefreshToken
- type Request
- type Response
- type RevocationRequest
- type Service
- func (s *Service) Exchange(ctx context.Context, req Request) (*Response, error)
- func (s *Service) Inspect(ctx context.Context, plaintext, hint string) (*Introspection, error)
- func (s *Service) Introspect(ctx context.Context, req IntrospectionRequest) (*Introspection, error)
- func (s *Service) Revoke(ctx context.Context, req RevocationRequest) error
- func (s *Service) RevokeToken(ctx context.Context, plaintext, hint string) (bool, error)
- type Store
Examples ¶
Constants ¶
const ( CodeInvalidRequest = "invalid_request" CodeInvalidClient = "invalid_client" CodeInvalidGrant = "invalid_grant" CodeUnsupportedGrantType = "unsupported_grant_type" CodeInvalidScope = "invalid_scope" // CodeInvalidTarget は resource パラメータが不正、または認可されていない Resource Server を // 指している(RFC 8707 §2.2)。 CodeInvalidTarget = "invalid_target" )
RFC 6749 §5.2 のエラーコード。
const ( HintAccessToken = "access_token" HintRefreshToken = "refresh_token" )
token_type_hint の値(RFC 7009 §2.1、RFC 7662 §2.1)。
const ( DefaultRefreshTokenTTL = 30 * 24 * time.Hour DefaultRefreshTokenIdleTTL = 14 * 24 * time.Hour )
Refresh Token の有効期間の既定値(ADR-0006 §4)。
- 絶対期限: Family 作成からの寿命。Rotation しても延びない。
- スライド期限: 最終使用からの寿命。Rotation のたびに延びるが、絶対期限を超えない。
const ( AccessTokenPrefix = "at_" RefreshTokenPrefix = "rt_" )
トークン平文の接頭辞。ログやシークレットスキャナでの識別用。
const JWTAccessTokenType = "at+jwt"
JWTAccessTokenType は JWT Access Token の typ ヘッダ(RFC 9068 §2.1)。 Resource Server はこれ以外(ID Token の "JWT" など)を拒否する(§4)。
Variables ¶
var ErrUnsupportedFormat = errors.New("token: no issuer for the requested access token format")
ErrUnsupportedFormat は、要求された形式を発行できる Issuer が配線されていない(設定不備。500)。
Functions ¶
This section is empty.
Types ¶
type AccessStatus ¶
type AccessStatus string
AccessStatus は Access Token の状態。
const ( AccessActive AccessStatus = "active" AccessRevoked AccessStatus = "revoked" )
type AccessToken ¶
type AccessToken struct {
Hash string
FamilyID string
GrantID string
ClientID string
Subject string
Scopes []string
// Audience はこのトークンが向けられる resource(RFC 8707 §2.2)。Family.Audience の部分集合。
// Introspection の aud に載せる。空なら認可サーバー自身(UserInfo)向け。
Audience []string
Status AccessStatus
IssuedAt time.Time
ExpiresAt time.Time
RevokedAt time.Time // ゼロ値なら未失効
}
AccessToken は Opaque な Access Token の記録。平文は保存しない。
type ByFormat ¶
ByFormat は形式ごとの Issuer に振り分ける。Engine が Opaque と JWT の 2 実装を配線する。
func (ByFormat) Issue ¶
func (b ByFormat) Issue(ctx context.Context, in IssueInput) (string, *AccessToken, error)
Issue は in.Format(空なら FormatOpaque)の Issuer に委譲する。なければ ErrUnsupportedFormat。
type Credentials ¶
type Credentials struct {
ClientID string
Secret string
// Method は資格情報の提示方法。Secret が無ければ client.AuthNone。
Method client.AuthMethod
}
Credentials はトークンエンドポイントで提示されたクライアントの資格情報(RFC 6749 §2.3)。
HTTP 層が Authorization ヘッダ(client_secret_basic)またはフォーム(client_secret_post、 public クライアントの client_id のみ)から取り出して渡す。複数の方式が同時に使われた場合は HTTP 層が invalid_request にする(RFC 6749 §2.3: MUST NOT use more than one method)。
type Error ¶
Error はトークンエンドポイントのプロトコルエラー(RFC 6749 §5.2)。 内部障害(Store の失敗など)は *Error ではなく通常の error で返し、HTTP 層が 500 にする。
func (*Error) HTTPStatus ¶
HTTPStatus は RFC 6749 §5.2 に従う HTTP ステータス。invalid_client は 401、他は 400。
type Family ¶
type Family struct {
ID string
GrantID string
ClientID string
Subject string
// Scopes はこの Family のトークンに付く scope。Grant の scope の部分集合。
Scopes []string
// Audience はこの Family のトークンが向けられる resource(RFC 8707)の上限。
// 認可要求の resource(Code の Resource)をそのまま引き継ぐ。Refresh で発行する Access Token の
// Audience はこの部分集合(ADR-0008 §3)。resource なしの要求では空。
Audience []string
// AuthenticatedAt / AMR / ACR は元の認証の情報。Refresh 時に発行する ID Token の
// auth_time / amr / acr は元の認証を表さなければならない(OIDC Core §12.2)ため、
// Family に写しを持つ。client_credentials の Family ではゼロ値。
AuthenticatedAt time.Time
AMR []string
ACR string
Status FamilyStatus
CreatedAt time.Time
// ExpiresAt は Refresh Token の絶対期限(Family 全体の寿命)。Refresh Token を
// 発行しない Family ではゼロ値。
ExpiresAt time.Time
RevokedAt time.Time // ゼロ値なら未失効
}
Family は 1 回の Code 交換(または client_credentials 要求)から派生するトークンの集まり。
type FamilyStatus ¶
type FamilyStatus string
FamilyStatus は Token Family の状態。
const ( FamilyActive FamilyStatus = "active" FamilyRevoked FamilyStatus = "revoked" )
type IDTokenInput ¶
type IDTokenInput struct {
Subject string
AuthenticatedAt time.Time
Nonce string
AMR []string
ACR string
// AccessToken は同時に発行した Access Token の平文(at_hash 用)。
AccessToken string
Scopes []string
}
IDTokenInput は ID Token の発行に必要な、トークンエンドポイントが持つ情報。
type IDTokenIssuer ¶
type IDTokenIssuer interface {
IssueIDToken(ctx context.Context, c *client.Client, in IDTokenInput) (string, error)
}
IDTokenIssuer は ID Token を発行する Port。oidc パッケージが実装し、Engine が配線する。 nil のままなら openid scope が付与されても id_token は返さない(OAuth 2.0 のみの構成)。
type Introspection ¶
type Introspection struct {
Active bool
// TokenType は Access Token なら "Bearer"。Refresh Token は RFC 6749 §5.1 の token_type を
// 持たないので空。
TokenType string
Scopes []string
// Audience はトークンが向けられる resource(RFC 7662 §2.2 の aud)。Access Token はそのトークンの、
// Refresh Token は Family の Audience。空なら認可サーバー自身向け。
Audience []string
ClientID string
Subject string
IssuedAt time.Time
ExpiresAt time.Time
// Kind は内部の種別(HintAccessToken / HintRefreshToken)。HTTP 応答には載せない。
Kind string
}
Introspection はトークンの状態(RFC 7662 §2.2)。Active が false なら他のフィールドは空。
type IntrospectionRequest ¶
type IntrospectionRequest struct {
Token string
TokenTypeHint string
Credentials Credentials
}
IntrospectionRequest は Introspection エンドポイントの要求(RFC 7662 §2.1)。
type IssueInput ¶
type IssueInput struct {
Client *client.Client
// Family はトークンが属する Token Family。GrantID・Subject と、認可コード由来の認証情報
// (AuthenticatedAt / AMR / ACR。client_credentials ではゼロ値)をここから取る。
Family *Family
// Scopes は Family.Scopes の、Audience は Family.Audience の部分集合(要求時の縮小を反映済み)。
Scopes []string
Audience []string
// Format は発行する形式。Audience の resource 設定で決まり、Audience が空なら FormatOpaque(ADR-0008 §3)。
Format resource.Format
// Alg は FormatJWT のときの署名アルゴリズム(resource.Resource.EffectiveAlg)。
Alg string
Now time.Time
}
IssueInput は Access Token の発行に必要な情報。Service が Code 交換・Refresh・client_credentials のそれぞれで組み立てる。
type Issuer ¶
type Issuer interface {
Issue(ctx context.Context, in IssueInput) (plaintext string, rec *AccessToken, err error)
}
Issuer は Access Token を発行する拡張点(ADR-0007 §4、ADR-0008 §3)。
戻り値の平文は応答にのみ使い、rec は Store に保存する記録。形式に依らず rec.Hash は 平文全体の SHA-256 で、Introspection と Revocation は同じ経路で動く。
type JWTIssuer ¶
type JWTIssuer struct {
// Issuer は iss クレーム(RFC 8414 の issuer と同じ値)。
Issuer string
Keys *keys.Set
}
JWTIssuer は RFC 9068 の JWT Access Token を発行する Issuer。
署名は keys の RS256 / ES256(ADR-0002。JOSE の利用は keys に閉じる)。JWT でも Store に AccessToken の行を持ち(Hash は JWT 文字列の SHA-256)、Introspection と Revocation は Opaque と 同じ経路で動く。ただし Resource Server が JWKS でローカル検証する限り、失効は exp まで伝播しない (ADR-0006 §1、ADR-0008 §3)。JWT を選ぶ resource は短い AccessTokenTTL の Client と組み合わせること。
Example ¶
ExampleJWTIssuer は、resource ごとに Access Token の形式を JWT(RFC 9068)にする。 JWTIssuer は Engine が配線するので、利用者は Config.Resources の AccessTokenFormat を選ぶだけでよい。 Resource Server は JWKS の鍵でローカルに検証できる。ただし失効は exp まで伝わらないので、 JWT を選ぶ resource には AccessTokenTTL の短い Client を組み合わせる(ADR-0008 §3)。
package main
import (
"context"
"fmt"
"log"
"log/slog"
"time"
authengine "github.com/u-masato/auth-engine"
"github.com/u-masato/auth-engine/client"
"github.com/u-masato/auth-engine/keys"
"github.com/u-masato/auth-engine/resource"
"github.com/u-masato/auth-engine/storage/memory"
"github.com/u-masato/auth-engine/token"
)
func main() {
ctx := context.Background()
now := time.Now()
store := memory.New()
svc, secret, err := client.NewMachine(client.MachineParams{
ID: "svc", Name: "Service", Scopes: []string{"files.read"}, AccessTokenTTL: 5 * time.Minute,
}, now)
if err != nil {
log.Fatal(err)
}
if err := store.CreateClient(ctx, svc); err != nil {
log.Fatal(err)
}
ks := keys.NewSet()
k, err := keys.Generate(keys.AlgRS256, now)
if err != nil {
log.Fatal(err)
}
if err := ks.AddSigning(k); err != nil {
log.Fatal(err)
}
engine, err := authengine.New(authengine.Config{
Issuer: "https://as.example.com", Store: store, Keys: ks, Logger: slog.New(slog.DiscardHandler),
Resources: []resource.Resource{{URI: "https://files.example.com", AccessTokenFormat: resource.FormatJWT}},
})
if err != nil {
log.Fatal(err)
}
res, err := engine.Token.Exchange(ctx, token.Request{
GrantType: "client_credentials",
Credentials: token.Credentials{ClientID: "svc", Secret: string(secret), Method: client.AuthClientSecretBasic},
Resource: []string{"https://files.example.com"},
})
if err != nil {
log.Fatal(err)
}
// Resource Server 側の検証。実際には /jwks から得た公開鍵で行い、
// typ・iss・aud・exp を確かめる(RFC 9068 §4)。
var claims struct {
Iss string `json:"iss"`
Aud []string `json:"aud"`
ClientID string `json:"client_id"`
Scope string `json:"scope"`
}
h, err := engine.Keys.Verify(res.AccessToken, &claims)
if err != nil {
log.Fatal(err)
}
fmt.Println(h.Type, h.Alg, claims.Iss, claims.Aud, claims.ClientID, claims.Scope, res.ExpiresIn)
// Opaque と同じく Store に記録されるので、Introspection と失効も使える。
info, err := engine.Token.Inspect(ctx, res.AccessToken, token.HintAccessToken)
if err != nil {
log.Fatal(err)
}
fmt.Println(info.Active, info.Audience)
}
Output: at+jwt RS256 https://as.example.com [https://files.example.com] svc files.read 300 true [https://files.example.com]
func (*JWTIssuer) Issue ¶
func (j *JWTIssuer) Issue(_ context.Context, in IssueInput) (string, *AccessToken, error)
Issue は in.Alg で署名した JWT を平文として返す。aud は in.Audience で、空なら発行しない (JWT は resource 向けにだけ使う。ADR-0008 §3)。
type OpaqueIssuer ¶
type OpaqueIssuer struct{}
OpaqueIssuer はランダム文字列の Access Token を発行する既定の Issuer(ADR-0006 §1)。
func (OpaqueIssuer) Issue ¶
func (OpaqueIssuer) Issue(_ context.Context, in IssueInput) (string, *AccessToken, error)
Issue は 256 bit 乱数の平文(接頭辞 at_)と、その SHA-256 を Hash に持つ記録を返す。
type RefreshStatus ¶
type RefreshStatus string
RefreshStatus は Refresh Token の状態。
const ( RefreshActive RefreshStatus = "active" RefreshRotated RefreshStatus = "rotated" RefreshRevoked RefreshStatus = "revoked" )
type RefreshToken ¶
type RefreshToken struct {
Hash string
FamilyID string
Status RefreshStatus
ReplacedByHash string // Status が rotated のとき、後継トークンの Hash
IssuedAt time.Time
ExpiresAt time.Time
RotatedAt time.Time // ゼロ値なら未 Rotation
RevokedAt time.Time // ゼロ値なら未失効
}
RefreshToken は Refresh Token の記録。Rotation で rotated になり、後継の Hash を持つ。
type Request ¶
type Request struct {
GrantType string
Credentials Credentials
// authorization_code
Code string
RedirectURI string
CodeVerifier string
// refresh_token
RefreshToken string
// Scope は refresh_token / client_credentials の scope パラメータ(空白区切り、未指定なら空)。
Scope string
// Resource は resource パラメータ(RFC 8707 §2.2。複数可)。発行する Access Token の Audience を
// 認可済みの範囲内で絞る。未指定なら認可済みの全部。
Resource []string
}
Request はトークン要求(RFC 6749 §4.1.3、§4.4.2、§6)。HTTP 層がフォームから組み立てる。
type Response ¶
type RevocationRequest ¶
type RevocationRequest struct {
Token string
TokenTypeHint string
Credentials Credentials
}
RevocationRequest は Revocation エンドポイントの要求(RFC 7009 §2.1)。
type Service ¶
type Service struct {
Clients client.Resolver
Store Store
// Grants は client_credentials が Grant を記録するのに使う。
Grants grant.Store
Clock clock.Clock
// Logger は監査イベント(発行、再利用検知)の出力先。nil なら slog.Default()。
Logger *slog.Logger
// IDTokens は openid scope が付与されたときに ID Token を発行する。nil なら発行しない。
IDTokens IDTokenIssuer
// Resources は resource パラメータ(RFC 8707 §2.2)で指定できる Resource Server の集合。
// authorization.Service と同じ Set を Engine が配線する。nil なら空集合。
Resources *resource.Set
// Issuer は Access Token を発行する(ADR-0007 §4)。Engine は形式ごとの ByFormat(Opaque と JWT)を
// 配線する。nil なら OpaqueIssuer だけで、JWT を要求する resource は内部エラーになる。
Issuer Issuer
// RefreshTokenTTL は Refresh Token の絶対期限、RefreshTokenIdleTTL はスライド期限。
// 0 なら既定値(DefaultRefreshTokenTTL / DefaultRefreshTokenIdleTTL)。
RefreshTokenTTL time.Duration
RefreshTokenIdleTTL time.Duration
}
Service はトークンエンドポイントのユースケース群。フィールドは Engine が配線する。
func (*Service) Inspect ¶
Inspect はトークン平文の状態を返す(Go API。ADR-0006 §5 の Inspect)。
不明・期限切れ・失効(Grant / Family の失効を含む)はすべて Active: false で、エラーにしない。 hint は探索順序のヒントで、外れていても両方の種別を探す。
Example ¶
ExampleService_Inspect は Go API でトークンの状態を調べる。Resource Server をエンジンと同じプロセスに 置くなら、HTTP の Introspection(RFC 7662)を経由せずにこれで検証できる。 不明・期限切れ・失効はすべて Active: false で返り、エラーにはならない。
package main
import (
"context"
"fmt"
"log"
"log/slog"
"time"
authengine "github.com/u-masato/auth-engine"
"github.com/u-masato/auth-engine/client"
"github.com/u-masato/auth-engine/keys"
"github.com/u-masato/auth-engine/storage/memory"
"github.com/u-masato/auth-engine/token"
)
// exampleEngine は Machine クライアント svc を登録した Engine と、svc に client_credentials で
// 発行した Access Token を返す。
func exampleEngine(ctx context.Context) (*authengine.Engine, string) {
now := time.Now()
store := memory.New()
svc, secret, err := client.NewMachine(client.MachineParams{ID: "svc", Name: "Service", Scopes: []string{"api.read"}}, now)
if err != nil {
log.Fatal(err)
}
if err := store.CreateClient(ctx, svc); err != nil {
log.Fatal(err)
}
ks := keys.NewSet()
k, err := keys.Generate(keys.AlgRS256, now)
if err != nil {
log.Fatal(err)
}
if err := ks.AddSigning(k); err != nil {
log.Fatal(err)
}
engine, err := authengine.New(authengine.Config{Issuer: "https://as.example.com", Store: store, Keys: ks, Logger: slog.New(slog.DiscardHandler)})
if err != nil {
log.Fatal(err)
}
res, err := engine.Token.Exchange(ctx, token.Request{
GrantType: "client_credentials",
Credentials: token.Credentials{ClientID: "svc", Secret: string(secret), Method: client.AuthClientSecretBasic},
})
if err != nil {
log.Fatal(err)
}
return engine, res.AccessToken
}
func main() {
ctx := context.Background()
engine, accessToken := exampleEngine(ctx)
info, err := engine.Token.Inspect(ctx, accessToken, token.HintAccessToken)
if err != nil {
log.Fatal(err) // 内部障害だけがエラーになる
}
fmt.Println(info.Active, info.TokenType, info.ClientID, info.Scopes)
info, err = engine.Token.Inspect(ctx, "at_unknown", "")
if err != nil {
log.Fatal(err)
}
fmt.Println(info.Active)
}
Output: true Bearer svc [api.read] false
func (*Service) Introspect ¶
func (s *Service) Introspect(ctx context.Context, req IntrospectionRequest) (*Introspection, error)
Introspect は Introspection エンドポイントの処理。
呼び出し元は confidential な Client(Web / Machine Profile)として認証しなければならない。 Resource Server は Machine Profile の Client として登録する想定(ADR-0006 §1)。 public な Client は自分を証明できないので invalid_client。認証に通れば、他 Client に 発行されたトークンも参照できる(Resource Server は任意の Client のトークンを検証する)。
func (*Service) Revoke ¶
func (s *Service) Revoke(ctx context.Context, req RevocationRequest) error
Revoke は Revocation エンドポイントの処理。
Client を認証し、そのトークンが提示した Client に発行されたものであれば失効させる。 他 Client のトークン・不明なトークン・失効済みのトークンに対しては何もせず成功を返す (RFC 7009 §2.2: invalid token は成功。他 Client のトークンについて存在を教えない)。
func (*Service) RevokeToken ¶
RevokeToken はトークン平文を失効させる(Go API。ADR-0006 §5 の RevokeToken)。
- Access Token: そのトークンだけを失効させる。同じ Family の Refresh Token は残る。
- Refresh Token: Family 全体(後継の Refresh Token と全 Access Token)を失効させる (RFC 7009 §2.1 の "SHOULD also invalidate all access tokens based on the same grant")。
戻り値はトークンが見つかったか。不明・失効済みでもエラーにしない。
Example ¶
ExampleService_RevokeToken は管理画面やサポート対応から、トークンを 1 本だけ失効させる。 Refresh Token を渡すと、その Token Family(後継の Refresh Token と全 Access Token)がまとめて失効する。 ユーザーと Client の連携そのものを解除するなら grant.Service.Revoke を使う。
package main
import (
"context"
"fmt"
"log"
"log/slog"
"time"
authengine "github.com/u-masato/auth-engine"
"github.com/u-masato/auth-engine/client"
"github.com/u-masato/auth-engine/keys"
"github.com/u-masato/auth-engine/storage/memory"
"github.com/u-masato/auth-engine/token"
)
// exampleEngine は Machine クライアント svc を登録した Engine と、svc に client_credentials で
// 発行した Access Token を返す。
func exampleEngine(ctx context.Context) (*authengine.Engine, string) {
now := time.Now()
store := memory.New()
svc, secret, err := client.NewMachine(client.MachineParams{ID: "svc", Name: "Service", Scopes: []string{"api.read"}}, now)
if err != nil {
log.Fatal(err)
}
if err := store.CreateClient(ctx, svc); err != nil {
log.Fatal(err)
}
ks := keys.NewSet()
k, err := keys.Generate(keys.AlgRS256, now)
if err != nil {
log.Fatal(err)
}
if err := ks.AddSigning(k); err != nil {
log.Fatal(err)
}
engine, err := authengine.New(authengine.Config{Issuer: "https://as.example.com", Store: store, Keys: ks, Logger: slog.New(slog.DiscardHandler)})
if err != nil {
log.Fatal(err)
}
res, err := engine.Token.Exchange(ctx, token.Request{
GrantType: "client_credentials",
Credentials: token.Credentials{ClientID: "svc", Secret: string(secret), Method: client.AuthClientSecretBasic},
})
if err != nil {
log.Fatal(err)
}
return engine, res.AccessToken
}
func main() {
ctx := context.Background()
engine, accessToken := exampleEngine(ctx)
found, err := engine.Token.RevokeToken(ctx, accessToken, token.HintAccessToken)
if err != nil {
log.Fatal(err)
}
info, err := engine.Token.Inspect(ctx, accessToken, token.HintAccessToken)
if err != nil {
log.Fatal(err)
}
fmt.Println("found:", found, "active:", info.Active)
}
Output: found: true active: false
type Store ¶
type Store interface {
// ConsumeAuthorizationCode は Code を消費済みにして内容を返す。
//
// - 未消費かつ未期限: 消費済みにして Code を返す。
// - 既に消費済み: 消費済みの Code と storage.ErrCodeAlreadyUsed を返す。
// 呼び出し側は Code.FamilyID の Family を失効させる。
// - 存在しない、または期限切れ: storage.ErrNotFound。
//
// 消費済みの Code は再利用検知のため、期限まで削除しない。
ConsumeAuthorizationCode(ctx context.Context, codeHash string, now time.Time) (*authorization.AuthorizationCode, error)
// IssueTokens は新しい Family と、それに属する最初のトークンを保存する。
// refresh は nil でよい。Family の GrantID が指す Grant が active でなければ
// storage.ErrGrantRevoked を返し、何も保存しない。
IssueTokens(ctx context.Context, family *Family, access *AccessToken, refresh *RefreshToken) error
// RotateRefreshToken は oldHash の Refresh Token を rotated にし、同じ Family に
// next(Refresh Token)と access(Access Token)を保存する。
//
// - oldHash が active かつ未期限: 成功。
// - oldHash が rotated: storage.ErrTokenReused。呼び出し側は Family を失効させる。
// - oldHash が revoked、または Family が revoked: storage.ErrFamilyRevoked。
// - Grant が revoked: storage.ErrGrantRevoked。
// - 存在しない、または期限切れ: storage.ErrNotFound。
RotateRefreshToken(ctx context.Context, oldHash string, next *RefreshToken, access *AccessToken, now time.Time) error
// LookupAccessToken は Access Token と、それが属する Grant を返す。
// Grant の失効チェックを二重化するため、Grant も併せて返す。なければ storage.ErrNotFound。
LookupAccessToken(ctx context.Context, hash string) (*AccessToken, *grant.Grant, error)
// LookupRefreshToken は Refresh Token を返す。なければ storage.ErrNotFound。
LookupRefreshToken(ctx context.Context, hash string) (*RefreshToken, error)
// GetTokenFamily は Family を返す。なければ storage.ErrNotFound。
// Refresh Token は Family を介して Client・subject・scope に結びつく。
GetTokenFamily(ctx context.Context, id string) (*Family, error)
// RevokeTokenFamily は Family と配下の全トークンを失効させる。
// 既に失効済みなら何もせず nil。存在しなければ storage.ErrNotFound。
RevokeTokenFamily(ctx context.Context, familyID string, now time.Time) error
// RevokeAccessToken は Access Token 1 つを失効させる。存在しなければ storage.ErrNotFound。
RevokeAccessToken(ctx context.Context, hash string, now time.Time) error
}
Store はトークンの永続化 Port。各メソッドは単体で原子的でなければならない(ADR-0005 §2)。