summaryrefslogtreecommitdiff
path: root/app/models/oauth2.go
blob: 65d37d4a90a2bff5b1a5b0e1544b2a24a8d418e2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package models

import (
	"crypto/rand"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/base64"
	"fmt"
)

const (
	DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code"
)

type AuthorizationRequest struct {
	Challenge       string            `url:"code_challenge" form:"code_challenge" json:"code_challenge"`                      // RFC7636
	ChallengeMethod PKCEChallengeType `url:"code_challenge_method" form:"code_challenge_method" json:"code_challenge_method"` // RFC7636
	ClientId        string            `url:"client_id" form:"client_id" json:"client_id"`
	Scope           string            `url:"scope" form:"scope" json:"scope"`
}

type DeviceAuthorizationResponse struct {
	DeviceCode              string `json:"device_code"`      // REQUIRED
	UserCode                string `json:"user_code"`        // REQUIRED
	VerificationUri         string `json:"verification_uri"` // REQUIRED
	VerificationUriComplete string `json:"verification_uri_complete,omitempty"`
	ExpiresIn               int    `json:"expires_in,omitempty"`
	Interval                int    `json:"interval,omitempty"`
}

type DeviceAccessTokenRequest struct {
	GrantType    string `url:"grant_type" form:"grant_type" json:"grant_type"`
	DeviceCode   string `url:"device_code" form:"device_code" json:"device_code"`
	ClientId     string `url:"client_id" form:"client_id" json:"client_id"`
	CodeVerifier string `url:"code_verifier" form:"code_verifier" json:"code_verifier"`
}

type AccessTokenResponse struct {
	AccessToken  string `json:"access_token"`
	TokenType    string `json:"token_type"`           // Must be Bearer
	ExpiresIn    string `json:"expires_in,omitempty"` // Lifetime in seconds
	RefreshToken string `json:"refresh_token,omitempty"`
	Scope        string `json:"scope,omitempty"`
}

type AuthorizationError string

const (
	ErrInvalidRequest       AuthorizationError = "invalid_request"
	ErrInvalidClient                           = "invalid_client"
	ErrInvalidGrant                            = "invalid_grant"
	ErrUnauthorizedClient                      = "unauthorized_client"
	ErrUnsupportedGrantType                    = "unsupported_grant_type"
	ErrInvalidScope                            = "invalid_scope"
	ErrAuthorizationPending                    = "authorization_pending" // RFC7636
	ErrSlowDown                                = "slow_down"             // RFC7636
	ErrAccessDenied                            = "access_denied"         // RFC7636
	ErrExpiredToken                            = "expired_token"         // RFC7636
)

type Oauth2Error struct {
	Type        AuthorizationError `json:"error"`
	Description string             `json:"error_description,omitempty"`
	Uri         string             `json:"error_uri,omitempty"`
}

func (e Oauth2Error) Error() string {
	if e.Description == "" {
		return fmt.Sprintf("Oauth2Error: %s", e.Type)
	} else {
		return fmt.Sprintf("Oauth2Error: %s %s", e.Type, e.Description)
	}
}

type PKCEChallengeType string

const (
	ChallengePlain PKCEChallengeType = "plain"
	ChallengeS256                    = "S256"
)

type PKCEChallenge struct {
	Verifier string
}

func NewPKCEChallenge() (*PKCEChallenge, error) {
	buf := make([]byte, 32)
	if _, err := rand.Read(buf); err != nil {
		return nil, err
	}
	return &PKCEChallenge{
		Verifier: base64.URLEncoding.EncodeToString(buf),
	}, nil
}

func (c *PKCEChallenge) Challenge() string {
	hash := sha256.Sum256([]byte(c.Verifier))
	return base64.URLEncoding.EncodeToString(hash[:])
}

func (c *PKCEChallenge) EqualString(o string) bool {
	return subtle.ConstantTimeCompare([]byte(o), []byte(c.Challenge())) != 1
}

const (
	GrantTypeAuthCode        = "authorization_code"                            // RFC7591
	GrantTypeImplicit        = "implicit"                                      // RFC7591
	GrantTypePassword        = "password"                                      // RFC7591
	GrantTypeClientCreds     = "client_credentials"                            // RFC7591
	GrantTypeRefreshToken    = "refresh_token"                                 // RFC7591
	GrantTypeBearerJwt       = "urn:ietf:params:oauth:grant-type:jwt-bearer"   // RFC7591
	GrantTypeBearerSaml      = "urn:ietf:params:oauth:grant-type:saml2-bearer" // RFC7591
	GrantTypeDevice          = "urn:ietf:params:oauth:grant-type:device_code"  // RFC8628
	ResponseTypeCode         = "code"                                          // RFC7591
	ResponseTypeToken        = "token"                                         // RFC7591
	ResponseModeQuery        = "query"                                         // RFC7591
	ResponseModeFragment     = "fragment"                                      // RFC7591
	ResponseModeFormPost     = "form_post"                                     // RFC7591
	ChallengeTypePlain       = "plain"                                         // RFC7636
	ChallengeTypeSHA256      = "S256"                                          // RFC7636
	Oauth2MetadataPath       = "/.well-known/oauth-authorization-server"
	Oauth2MetadataCompatPath = "/.well-known/openid-configuration"
)

// All options are required unless omitempty
type OauthDiscoveryMetadata struct {
	Issuer                            string   `json:"issuer"`                                                             // RFC88414, https url w/no query/fragment
	AuthorizationEndpoint             string   `json:"authorization_endpoint"`                                             // RFC88414
	TokenEndpoint                     string   `json:"token_endpoint"`                                                     // RFC88414
	SupportedResponseTypes            []string `json:"response_types_supported"`                                           // RFC88414
	JWKSUri                           string   `json:"jwks_uri,omitempty"`                                                 // RFC88414
	RegistrationEndpoint              string   `json:"registration_endpoint,omitempty"`                                    // RFC88414
	SupportedScopes                   []string `json:"scopes_supported,omitempty"`                                         // RFC88414
	SupportedResponseModes            []string `json:"response_modes_supported,omitempty"`                                 // RFC88414
	SupportedGrantTypes               []string `json:"grant_types_supported,omitempty"`                                    // RFC88414, default: authorization_code, implicit
	SupportedAuthMethods              []string `json:"token_endpoint_auth_methods_supported,omitempty"`                    // RFC88414
	SupportedSigningAlgs              []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`         // RFC88414
	SupportedUILocales                []string `json:"ui_locales_supported,omitempty"`                                     // RFC88414, RFC5646 codes
	PolicyUri                         string   `json:"op_policy_uri,omitempty"`                                            // RFC88414
	TosUri                            string   `json:"op_tos_uri,omitempty"`                                               // RFC88414
	RevocationEndpoint                string   `json:"revocation_endpoint,omitempty"`                                      // RFC88414
	SupportedRevocationAuthMethods    []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`               // RFC88414
	SupportedRevocationSigningAlgs    []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`    // RFC88414
	IntrospectionEndpoint             string   `json:"introspection_endpoint,omitempty"`                                   // RFC88414
	SupportedIntrospectionAuthMethods []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`            // RFC88414
	SupportedIntrospectionSigningAlgs []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"` // RFC88414
	SupportedChallengeCodeMethods     []string `json:"code_challenge_methods_supported,omitempty"`                         // RFC88414
	ServiceDocumentation              string   `json:"service_documentation,omitempty"`                                    // RFC88414
	DeviceAuthorizationEndpoint       string   `json:"device_authorization_endpoint,omitempty"`                            // RFC8628
}