Documentation
¶
Index ¶
- Constants
- Variables
- func AccessTokenTTL(cfgs configuration.ConfigurationInterface) (time.Duration, error)
- func AuthenticationOauth2PasswordHandler(c echo.Context) error
- func DeleteAccessToken(c *bolo.RequestContext, accessToken string) error
- func GetAccessToken(accessToken string) (string, error)
- func GetOauth2TokenFromAuthorization(authorization string) string
- func GetRefreshToken(refreshToken string) (string, error)deprecated
- func HandleRequestAuthentication(c echo.Context) error
- func InitStorage(app bolo.App)
- func IsPublicRoute(url string) bool
- func NewUnauthorizedTokenHTTPError(c echo.Context, description string) error
- func Oauth2FindUserWithToken(accessToken string) (bolo.UserInterface, error)
- func ParseTTL(value string, fallback time.Duration) (time.Duration, error)
- func RefreshAbsoluteTTL(cfgs configuration.ConfigurationInterface) (time.Duration, error)
- func RefreshIdleTTL(cfgs configuration.ConfigurationInterface) (time.Duration, error)
- func RefreshReuseGrace(cfgs configuration.ConfigurationInterface) (time.Duration, error)
- func RevokeByRefreshToken(token string) error
- func RevokeOauth2TokenHandler(c echo.Context) error
- func RevokeRefreshFamily(familyID string) error
- func RotateRefreshToken(c echo.Context, refreshToken string) (*TokenPair, *user_models.UserModel, *RefreshError)
- func RotateRefreshTokenWithFormat(c echo.Context, refreshToken string, format AccessTokenFormat) (*TokenPair, *user_models.UserModel, *RefreshError)
- func SetAccessToken(c *bolo.RequestContext, accessToken string, value string) error
- func SetJWTAccessGenerator(gen JWTAccessGenerator)
- func SetRefreshToken(c *bolo.RequestContext, refreshToken string, value string) error
- func Strict401Enabled(cfgs configuration.ConfigurationInterface) bool
- func ValidUsernamePassword(username, password string) (bool, error)
- type AccessTokenFormat
- type ForbiddenHTTPError
- func (e *ForbiddenHTTPError) Error() string
- func (e *ForbiddenHTTPError) GetCode() int
- func (e *ForbiddenHTTPError) GetInternal() error
- func (e *ForbiddenHTTPError) GetMessage() interface{}
- func (e *ForbiddenHTTPError) SetCode(code int) error
- func (e *ForbiddenHTTPError) SetInternal(internal error) error
- func (e *ForbiddenHTTPError) SetMessage(message interface{}) error
- type JWTAccessGenerator
- type Oauth2PasswordPlugin
- type Oauth2TokenData
- func Oauth2GenerateAndSaveToken(ctx *bolo.RequestContext, user bolo.UserInterface) (Oauth2TokenData, error)
- func Oauth2GenerateAndSaveTokenJWT(reqCtx *bolo.RequestContext, user bolo.UserInterface) (Oauth2TokenData, error)
- func Oauth2GenerateToken(ctx *bolo.RequestContext, u bolo.UserInterface) (Oauth2TokenData, error)
- type PluginCfgs
- type RefreshErrKind
- type RefreshError
- type RefreshTokenRecord
- type RevokeTokenRequestBody
- type TokenPair
- type UnauthorizedHTTPError
Constants ¶
const ( DefaultAccessTokenTTL = 30 * time.Minute DefaultRefreshIdleTTL = 72 * time.Hour DefaultRefreshAbsoluteTTL = 720 * time.Hour // 30 dias DefaultRefreshReuseGrace = 30 * time.Second )
TTLs padrão usados na cascata de configuração: valem quando nenhuma variável de ambiente (nova ou legada) é encontrada.
const ( RefreshTokenKeyPrefix = "RT:" // refresh token ativo (formato novo) RefreshTokenUsedKeyPrefix = "RTUSED:" // par já emitido dentro da graça de reuso RefreshTokenTombKeyPrefix = "RTTOMB:" // marca o token como usado (detecta reuso fora da graça) RefreshTokenFamilyKeyPrefix = "RTFAM:" // SET com os refresh tokens da família )
Prefixos das chaves Redis do fluxo de refresh seguro (mesmo Redis/DB do storage atual). Tokens legados continuam gravados sem prefixo e lidos em dupla para não derrubar sessões vivas no deploy. Exportados para que os consumidores (e suites de teste) não dupliquem literais sujeitos a drift.
Variables ¶
var ( // StorageDBWriter - Oauth tokens redis cache connection StorageDBWriter *redis.Client // StorageDBReader - Oauth tokens redis cache connection StorageDBReader *redis.Client )
Functions ¶
func AccessTokenTTL ¶
func AccessTokenTTL(cfgs configuration.ConfigurationInterface) (time.Duration, error)
AccessTokenTTL lê o TTL do access token em cascata: OAUTH2_ACCESS_TOKEN_TTL (string com unidade, ex.: "30m") => OAUTH2_ACCESS_TOKEN_EXPIRATION (inteiro em MINUTOS, comportamento legado, loga warn de deprecação) => 30m.
func DeleteAccessToken ¶
func DeleteAccessToken(c *bolo.RequestContext, accessToken string) error
func GetAccessToken ¶
func GetOauth2TokenFromAuthorization ¶
GetOauth2TokenFromAuthorization extrai o token oauth2 do header Authorization. Aceita SOMENTE o prefixo "Bearer": outros esquemas (ex.: "Basic") não carregam um access token oauth2 e devolvem "".
func GetRefreshToken
deprecated
func IsPublicRoute ¶
func NewUnauthorizedTokenHTTPError ¶
NewUnauthorizedTokenHTTPError monta o 401 do modo strict: registra o header WWW-Authenticate na resposta e devolve o erro compatível com o handler do bolo (que renderiza o corpo no formato BaseErrorResponse para JSON).
func Oauth2FindUserWithToken ¶
func Oauth2FindUserWithToken(accessToken string) (bolo.UserInterface, error)
func ParseTTL ¶
ParseTTL converte um valor de duração com unidade (ex.: "30m", "7d", "2d12h") em time.Duration. Além das unidades de time.ParseDuration aceita o sufixo "d" de dias (7d => 168h). Valor vazio devolve o fallback; valor inválido devolve erro explícito — configuração quebrada não pode ser silenciada.
func RefreshAbsoluteTTL ¶
func RefreshAbsoluteTTL(cfgs configuration.ConfigurationInterface) (time.Duration, error)
RefreshAbsoluteTTL lê o teto absoluto da família de refresh tokens: mesmo com rotações contínuas a família morre nesse prazo (OAUTH2_REFRESH_ABSOLUTE_TTL, default 720h/30 dias).
func RefreshIdleTTL ¶
func RefreshIdleTTL(cfgs configuration.ConfigurationInterface) (time.Duration, error)
RefreshIdleTTL lê o TTL idle do refresh token (renovado a cada rotação) em cascata: OAUTH2_REFRESH_IDLE_TTL (com unidade) => OAUTH2_REFRESH_TOKEN_EXPIRATION (inteiro em MINUTOS, legado) => 72h.
func RefreshReuseGrace ¶
func RefreshReuseGrace(cfgs configuration.ConfigurationInterface) (time.Duration, error)
RefreshReuseGrace lê a janela de graça do reuso idempotente do refresh token: dentro dela, replay do token antigo devolve o MESMO par emitido (OAUTH2_REFRESH_REUSE_GRACE, default 30s).
func RevokeByRefreshToken ¶
RevokeByRefreshToken resolve a família à qual o token pertence (ativo, já usado ou legado) e revoga tudo. Token desconhecido não é erro (best-effort).
func RevokeOauth2TokenHandler ¶
RevokeOauth2TokenHandler revoga um refresh token (hint "refresh_token" ou ausente) ou um access token (hint "access_token"). Seguindo a RFC 7009, responde 200 SEMPRE para token de formato válido, sem revelar se o token existia; falha de revogação é apenas logada (best-effort).
func RevokeRefreshFamily ¶
RevokeRefreshFamily revoga todos os tokens de uma família: refresh tokens ativos, pares em graça e tombstones. Usado em reuso detectado, teto absoluto vencido e revogação explícita (logout/revoke). Todas as chaves são removidas num único DEL multi-key (1 round trip + 1 por índice).
func RotateRefreshToken ¶
func RotateRefreshToken(c echo.Context, refreshToken string) (*TokenPair, *user_models.UserModel, *RefreshError)
RotateRefreshToken troca um refresh token válido por um par novo (formato opaco legado), com rotação single-use e graça de reuso idempotente. Veja RotateRefreshTokenWithFormat para a semântica completa.
func RotateRefreshTokenWithFormat ¶
func RotateRefreshTokenWithFormat(c echo.Context, refreshToken string, format AccessTokenFormat) (*TokenPair, *user_models.UserModel, *RefreshError)
RotateRefreshTokenWithFormat troca um refresh token válido por um par novo no formato pedido, com rotação single-use e graça de reuso idempotente:
- RTUSED hit (dentro da graça) => devolve o MESMO par já emitido, sem nova rotação (a aba que perdeu a corrida também recebe 200);
- RT: hit (formato novo) ou chave legada sem prefixo => valida o teto absoluto e o usuário, gera par novo, indexa a família, marca o token antigo como usado (RTUSED + RTTOMB) e o consome; o token legado migra para o formato novo;
- miss => se houver tombstone é reuso fora da graça (revoga a família e retorna ErrReuseDetected); sem tombstone é sessão vencida por idle (ErrExpiredIdle).
Com FormatJWT o access novo é um JWT stateless (não gravado no Redis); o par publicado na graça (RTUSED) carrega a string JWT sem alteração, então o replay dentro da graça devolve o MESMO JWT. O refresh token do par é sempre opaco — a string de acesso é transparente para graça/famílias.
Erros de infra (Redis fora, dados corrompidos, config inválida) vêm com Kind ErrInternal e NÃO revogam nada — o chamador decide o 500.
func SetAccessToken ¶
func SetAccessToken(c *bolo.RequestContext, accessToken string, value string) error
func SetJWTAccessGenerator ¶
func SetJWTAccessGenerator(gen JWTAccessGenerator)
SetJWTAccessGenerator registra o gerador de access tokens JWT usado por Oauth2GenerateAndSaveTokenJWT e RotateRefreshTokenWithFormat.
func SetRefreshToken ¶
func SetRefreshToken(c *bolo.RequestContext, refreshToken string, value string) error
func Strict401Enabled ¶
func Strict401Enabled(cfgs configuration.ConfigurationInterface) bool
Strict401Enabled indica se o middleware deve responder 401 explícito (com WWW-Authenticate) para access token inexistente/expirado em vez de seguir a requisição como anônima (OAUTH2_STRICT_401, default false).
func ValidUsernamePassword ¶
Types ¶
type AccessTokenFormat ¶
type AccessTokenFormat int
AccessTokenFormat seleciona o formato do access token emitido nos fluxos de geração/rotação de tokens.
const ( // FormatOpaque: access token opaco (uuid + random) gravado no Redis e // validado pelo middleware oauth2TokenAuthentication. Fluxo legado. FormatOpaque AccessTokenFormat = iota // FormatJWT: access JWT stateless — NÃO é gravado no Redis; a validação é // criptográfica (middleware do subplugin oauth2_jwt). Apenas o refresh // token mantém estado no Redis. FormatJWT )
type ForbiddenHTTPError ¶
type ForbiddenHTTPError struct {
Code int `json:"code"`
Message interface{} `json:"message"`
Internal error `json:"-"` // Stores the error returned by an external dependency
ErrorMessage string `json:"error"`
ErrorContext string `json:"error_context"`
}
func (*ForbiddenHTTPError) Error ¶
func (e *ForbiddenHTTPError) Error() string
Error makes it compatible with `error` interface.
func (*ForbiddenHTTPError) GetCode ¶
func (e *ForbiddenHTTPError) GetCode() int
func (*ForbiddenHTTPError) GetInternal ¶
func (e *ForbiddenHTTPError) GetInternal() error
func (*ForbiddenHTTPError) GetMessage ¶
func (e *ForbiddenHTTPError) GetMessage() interface{}
func (*ForbiddenHTTPError) SetCode ¶
func (e *ForbiddenHTTPError) SetCode(code int) error
func (*ForbiddenHTTPError) SetInternal ¶
func (e *ForbiddenHTTPError) SetInternal(internal error) error
func (*ForbiddenHTTPError) SetMessage ¶
func (e *ForbiddenHTTPError) SetMessage(message interface{}) error
type JWTAccessGenerator ¶
type JWTAccessGenerator func(ctx *bolo.RequestContext, u bolo.UserInterface) (accessToken string, expiresIn int64, err error)
JWTAccessGenerator emite o access token no formato JWT. Registrado pelo subplugin oauth2_jwt no evento configuration (dono do segredo e dos claims); este pacote permanece agnóstico aos detalhes criptográficos.
type Oauth2PasswordPlugin ¶
func NewPlugin ¶
func NewPlugin(cfg *PluginCfgs) *Oauth2PasswordPlugin
func (*Oauth2PasswordPlugin) BindRoutes ¶
func (r *Oauth2PasswordPlugin) BindRoutes(app bolo.App) error
func (*Oauth2PasswordPlugin) GetMigrations ¶
func (r *Oauth2PasswordPlugin) GetMigrations() []*bolo.Migration
func (*Oauth2PasswordPlugin) GetName ¶
func (p *Oauth2PasswordPlugin) GetName() string
type Oauth2TokenData ¶
type Oauth2TokenData struct {
ID string `json:"id"`
OwnerId json.Number `json:"ownerId"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
Scopes []string `json:"scopes"`
ExpireDate time.Time `json:"expireDate"`
ExpiresIn int64 `json:"expiresIn"`
}
func Oauth2GenerateAndSaveToken ¶
func Oauth2GenerateAndSaveToken(ctx *bolo.RequestContext, user bolo.UserInterface) (Oauth2TokenData, error)
func Oauth2GenerateAndSaveTokenJWT ¶
func Oauth2GenerateAndSaveTokenJWT(reqCtx *bolo.RequestContext, user bolo.UserInterface) (Oauth2TokenData, error)
Oauth2GenerateAndSaveTokenJWT emite o par no formato JWT: access token stateless (validação criptográfica, NÃO gravado no Redis) + refresh token opaco rotativo já no formato novo (RT: com família e teto absoluto desde o login — a primeira rotação apenas continua a família).
Exige o gerador JWT registrado via SetJWTAccessGenerator (feito pelo subplugin oauth2_jwt no evento configuration); sem ele devolve erro explícito em vez de emitir token em formato desconhecido.
func Oauth2GenerateToken ¶
func Oauth2GenerateToken(ctx *bolo.RequestContext, u bolo.UserInterface) (Oauth2TokenData, error)
func (*Oauth2TokenData) IsValid ¶
func (r *Oauth2TokenData) IsValid() bool
type PluginCfgs ¶
type PluginCfgs struct{}
type RefreshErrKind ¶
type RefreshErrKind int
RefreshErrKind classifica a falha do fluxo de refresh para o chamador mapear em status/mensagens HTTP sem conhecer detalhes de Redis.
const ( // ErrInternal - erro de infra (Redis fora, dados corrompidos, config // inválida): o chamador deve responder 500 e NADA é revogado. ErrInternal RefreshErrKind = iota // ErrExpiredIdle - token nunca usado e vencido pelo tempo de inatividade. ErrExpiredIdle // ErrExpiredAbsolute - família venceu o teto absoluto desde o primeiro token. ErrExpiredAbsolute // ErrReuseDetected - token já usado reapresentado fora da graça: possível // roubo, a família inteira é revogada. ErrReuseDetected // ErrUserInvalid - dono do token não existe mais, está inativo ou bloqueado. ErrUserInvalid )
func (RefreshErrKind) String ¶
func (k RefreshErrKind) String() string
String devolve um rótulo curto do tipo de erro para logs e respostas.
type RefreshError ¶
type RefreshError struct {
Kind RefreshErrKind
Err error
}
RefreshError é o erro retornado pelas funções de refresh/revogação. Err carrega a causa original quando existe (erros de infra).
func (*RefreshError) Error ¶
func (e *RefreshError) Error() string
Error torna o tipo compatível com a interface `error`.
func (*RefreshError) Unwrap ¶
func (e *RefreshError) Unwrap() error
Unwrap expõe a causa original para errors.Is/As.
type RefreshTokenRecord ¶
type RefreshTokenRecord struct {
OwnerID json.Number `json:"ownerId"`
FamilyID string `json:"familyId"` // uuid da família de tokens
// CreatedAt é diagnóstico/forense (quando cada token da família foi
// emitido); nenhum fluxo decide por ele.
CreatedAt time.Time `json:"createdAt"`
AbsoluteDeadline time.Time `json:"absoluteDeadline"`
}
RefreshTokenRecord é o conteúdo persistido em RT:<refresh token>. É o formato novo, com família e teto absoluto.
type RevokeTokenRequestBody ¶
type RevokeTokenRequestBody struct {
Token string `json:"token" validate:"required"`
TokenTypeHint string `json:"token_type_hint"`
}
RevokeTokenRequestBody corpo do endpoint de revogação (RFC 7009).
type TokenPair ¶
type TokenPair struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int64 `json:"expires_in"`
}
TokenPair é o par oauth2 devolvido nas rotações (mesmo formato do corpo da resposta do grant password).
type UnauthorizedHTTPError ¶
type UnauthorizedHTTPError struct {
}
UnauthorizedHTTPError é o erro do modo strict 401: token Bearer inexistente/expirado responde 401 com header WWW-Authenticate (RFC 6750) e corpo no formato BaseErrorResponse. Embute ForbiddenHTTPError para não duplicar o contrato de corpo/JSON (mesma forma serializada; apenas a semântica do código difere — 401 via factory abaixo).