Files
de61635b81 refactor: move all packages under pkg/ for v5 (#559)
* refactor: move all packages under pkg/ for v5

Move all 13 top-level Go packages into pkg/ for a cleaner repository
structure. All internal import paths updated from
github.com/formancehq/go-libs/v5/<pkg> to
github.com/formancehq/go-libs/v5/pkg/<pkg>.

Also includes cleanup:
- Fix .golangci.yml stale v4 CircuitBreaker path
- Replace deprecated str.Contains with stdlib slices.Contains
- Update TODO(v4) markers to TODO
- Delete stale coverage.txt
- Fix pgtesting timeout not being propagated to docker pool
- Update docs/ARCHITECTURE.md to reflect new structure

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* chore: remove compatibility aliases, unused params, and stub files

- Remove unused type aliases (Authenticator, KeySet) from authnfx
- Remove unused `debug` parameter from S3ModuleFromFlags
- Delete comment-only stub files in messaging/publish/circuit/

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* chore: rename cli.go to flags.go for consistency

Standardize naming across all packages: cli.go -> flags.go
since these files all serve the same purpose (flag definitions).

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* chore: rename

* chore(deps): update all dependencies

Co-Authored-By: Claude Opus 4.6 <[email protected]>

* fix: resolve package name mismatches in generated mocks

Generated mock files had stale package names from before the pkg/ move,
causing import cycles and build failures with --build-tags it.

Co-Authored-By: Claude Opus 4.6 <[email protected]>

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
2026-03-20 09:17:12 +00:00

230 lines
6.0 KiB
Go

package oidc
import (
"encoding/json"
"errors"
"fmt"
)
type errorType string
const (
InvalidRequest errorType = "invalid_request"
InvalidScope errorType = "invalid_scope"
InvalidClient errorType = "invalid_client"
InvalidGrant errorType = "invalid_grant"
InvalidToken errorType = "invalid_token"
UnauthorizedClient errorType = "unauthorized_client"
UnsupportedGrantType errorType = "unsupported_grant_type"
ServerError errorType = "server_error"
InteractionRequired errorType = "interaction_required"
LoginRequired errorType = "login_required"
RequestNotSupported errorType = "request_not_supported"
// Additional error codes as defined in
// https://www.rfc-editor.org/rfc/rfc8628#section-3.5
// Device Access Token Response
AuthorizationPending errorType = "authorization_pending"
SlowDown errorType = "slow_down"
AccessDenied errorType = "access_denied"
ExpiredToken errorType = "expired_token"
// InvalidTarget error is returned by Token Exchange if
// the requested target or audience is invalid.
// [RFC 8693, Section 2.2.2: Error Response](https://www.rfc-editor.org/rfc/rfc8693#section-2.2.2)
InvalidTarget errorType = "invalid_target"
)
var (
ErrInvalidToken = func() *Error {
return &Error{
ErrorType: InvalidToken,
}
}
ErrInvalidRequest = func() *Error {
return &Error{
ErrorType: InvalidRequest,
}
}
ErrInvalidRequestRedirectURI = func() *Error {
return &Error{
ErrorType: InvalidRequest,
redirectDisabled: true,
}
}
ErrInvalidScope = func() *Error {
return &Error{
ErrorType: InvalidScope,
}
}
ErrInvalidClient = func() *Error {
return &Error{
ErrorType: InvalidClient,
redirectDisabled: true,
}
}
ErrInvalidGrant = func() *Error {
return &Error{
ErrorType: InvalidGrant,
}
}
ErrUnauthorizedClient = func() *Error {
return &Error{
ErrorType: UnauthorizedClient,
}
}
ErrUnsupportedGrantType = func() *Error {
return &Error{
ErrorType: UnsupportedGrantType,
}
}
ErrServerError = func() *Error {
return &Error{
ErrorType: ServerError,
}
}
ErrInteractionRequired = func() *Error {
return &Error{
ErrorType: InteractionRequired,
}
}
ErrLoginRequired = func() *Error {
return &Error{
ErrorType: LoginRequired,
}
}
ErrRequestNotSupported = func() *Error {
return &Error{
ErrorType: RequestNotSupported,
}
}
// Device Access Token errors:
ErrAuthorizationPending = func() *Error {
return &Error{
ErrorType: AuthorizationPending,
Description: "The client SHOULD repeat the access token request to the token endpoint, after interval from device authorization response.",
}
}
ErrSlowDown = func() *Error {
return &Error{
ErrorType: SlowDown,
Description: "Polling should continue, but the interval MUST be increased by 5 seconds for this and all subsequent requests.",
}
}
ErrAccessDenied = func() *Error {
return &Error{
ErrorType: AccessDenied,
Description: "The authorization request was denied.",
}
}
ErrExpiredDeviceCode = func() *Error {
return &Error{
ErrorType: ExpiredToken,
Description: "The \"device_code\" has expired.",
}
}
// Token exchange error
ErrInvalidTarget = func() *Error {
return &Error{
ErrorType: InvalidTarget,
Description: "The requested audience or target is invalid.",
}
}
)
type Error struct {
Parent error `json:"-" schema:"-"`
ErrorType errorType `json:"error" schema:"error"`
Description string `json:"error_description,omitempty" schema:"error_description,omitempty"`
State string `json:"state,omitempty" schema:"state,omitempty"`
redirectDisabled bool `schema:"-"`
returnParent bool `schema:"-"`
}
func (e *Error) MarshalJSON() ([]byte, error) {
m := struct {
Error errorType `json:"error"`
ErrorDescription string `json:"error_description,omitempty"`
State string `json:"state,omitempty"`
Parent string `json:"parent,omitempty"`
}{
Error: e.ErrorType,
ErrorDescription: e.Description,
State: e.State,
}
if e.returnParent && e.Parent != nil {
m.Parent = e.Parent.Error()
}
return json.Marshal(m)
}
func (e *Error) Error() string {
message := "ErrorType=" + string(e.ErrorType)
if e.Description != "" {
message += " Description=" + e.Description
}
if e.Parent != nil {
message += " Parent=" + e.Parent.Error()
}
return message
}
func (e *Error) Unwrap() error {
return e.Parent
}
func (e *Error) Is(target error) bool {
t, ok := target.(*Error)
if !ok {
return false
}
return e.ErrorType == t.ErrorType &&
(e.Description == t.Description || t.Description == "") &&
(e.State == t.State || t.State == "")
}
func (e *Error) WithParent(err error) *Error {
e.Parent = err
return e
}
// WithReturnParentToClient allows returning the set parent error to the HTTP client.
// Currently it only supports setting the parent inside JSON responses, not redirect URLs.
// As Go errors don't unmarshal well, only the marshaller is implemented for the moment.
//
// Warning: parent errors may contain sensitive data or unwanted details about the server status.
// Also, the `parent` field is not a standard error field and might confuse certain clients
// that require fully compliant responses.
func (e *Error) WithReturnParentToClient(b bool) *Error {
e.returnParent = b
return e
}
func (e *Error) WithDescription(desc string, args ...any) *Error {
e.Description = fmt.Sprintf(desc, args...)
return e
}
func (e *Error) WithRedirectDisabled() *Error {
e.redirectDisabled = true
return e
}
func (e *Error) IsRedirectDisabled() bool {
return e.redirectDisabled
}
// DefaultToServerError checks if the error is an Error
// if not the provided error will be wrapped into a ServerError
func DefaultToServerError(err error, description string) *Error {
oauth := new(Error)
if ok := errors.As(err, &oauth); !ok {
oauth.ErrorType = ServerError
oauth.Description = description
oauth.Parent = err
}
return oauth
}