Files
probo/pkg/iam/oauth2server/errors.go
Émile Ré 9156d6a16a Add wsl linter and fix
Signed-off-by: Émile Ré <emile@probo.com>
2026-05-20 09:27:28 +04:00

114 lines
3.5 KiB
Go

// Copyright (c) 2026 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package oauth2server
import (
"errors"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
)
// OAuth2Error represents an OAuth2 protocol error with an associated
// error code per RFC 6749 §5.2 and RFC 8628 §3.5.
type OAuth2Error struct {
code string
description string
}
func (e *OAuth2Error) Error() string {
if e.description != "" {
return e.code + ": " + e.description
}
return e.code
}
func (e *OAuth2Error) ErrorCode() string { return e.code }
func (e *OAuth2Error) Description() string { return e.description }
func (e *OAuth2Error) Is(target error) bool {
t, ok := target.(*OAuth2Error)
if !ok {
return false
}
return e.code == t.code
}
type ErrorOption func(*OAuth2Error)
func WithDescription(description string) ErrorOption {
return func(e *OAuth2Error) {
e.description = description
}
}
func WithError(err error) ErrorOption {
return func(e *OAuth2Error) {
e.description = err.Error()
}
}
// NewError creates a new OAuth2Error derived from a sentinel error code.
func NewError(code *OAuth2Error, opts ...ErrorOption) *OAuth2Error {
e := &OAuth2Error{code: code.code}
for _, opt := range opts {
opt(e)
}
return e
}
var (
// OAuth2 error codes per RFC 6749 §5.2 and RFC 8628 §3.5.
ErrInvalidRequest = &OAuth2Error{code: "invalid_request"}
ErrInvalidClient = &OAuth2Error{code: "invalid_client"}
ErrInvalidGrant = &OAuth2Error{code: "invalid_grant"}
ErrUnauthorizedClient = &OAuth2Error{code: "unauthorized_client"}
ErrUnsupportedGrantType = &OAuth2Error{code: "unsupported_grant_type"}
ErrInvalidScope = &OAuth2Error{code: "invalid_scope"}
ErrAccessDenied = &OAuth2Error{code: "access_denied"}
ErrServerError = &OAuth2Error{code: "server_error"}
ErrInvalidRedirectURI = &OAuth2Error{code: "invalid_redirect_uri"}
// RFC 7009 revocation errors.
ErrUnsupportedTokenType = &OAuth2Error{code: "unsupported_token_type"}
// RFC 8628 device flow errors.
ErrAuthorizationPending = &OAuth2Error{code: "authorization_pending"}
ErrSlowDown = &OAuth2Error{code: "slow_down"}
ErrExpiredToken = &OAuth2Error{code: "expired_token"}
)
var (
ErrClientNotFound = errors.New("client not found")
ErrConsentNotFound = errors.New("consent not found")
ErrDeviceCodeNotPending = errors.New("device code is not pending")
ErrUnauthorizedMember = errors.New("user is not a member of the client organization")
)
// ConsentRequiredError is returned by Authorize when the user must approve
// the authorization request before a code can be issued.
type ConsentRequiredError struct {
ConsentID gid.GID
Client *coredata.OAuth2Client
Scopes coredata.OAuth2Scopes
}
func (e *ConsentRequiredError) Error() string {
return "consent required"
}