fork of haileyok/atproto-oauth-golang
1package oauth
2
3import (
4 "encoding/json"
5 "fmt"
6 "net/url"
7 "slices"
8)
9
10type TokenResponse struct {
11 DpopAuthserverNonce string `json:"-"`
12 AccessToken string `json:"access_token"`
13 RefreshToken string `json:"refresh_token"`
14 ExpiresIn int `json:"expires_in"`
15 Scope string `json:"scope"`
16 Sub string `json:"sub"`
17 TokenType string `json:"token_type"`
18}
19
20type RefreshTokenArgs struct {
21 AuthserverUrl string
22 RefreshToken string
23 DpopPrivateJwk string
24 DpopAuthserverNonce string
25}
26
27type SendParAuthResponse struct {
28 PkceVerifier string
29 State string
30 DpopAuthserverNonce string
31 ExpiresIn float64
32 RequestUri string
33}
34
35type OauthProtectedResource struct {
36 Resource string `json:"resource"`
37 AuthorizationServers []string `json:"authorization_servers"`
38 ScopesSupported []string `json:"scopes_supported"`
39 BearerMethodsSupported []string `json:"bearer_methods_supported"`
40 ResourceDocumentation string `json:"resource_documentation"`
41}
42
43func (opr *OauthProtectedResource) UnmarshalJSON(b []byte) error {
44 type Tmp OauthProtectedResource
45 var tmp Tmp
46
47 if err := json.Unmarshal(b, &tmp); err != nil {
48 return err
49 }
50
51 *opr = OauthProtectedResource(tmp)
52
53 return nil
54}
55
56type OauthAuthorizationMetadata struct {
57 Issuer string `json:"issuer"`
58 RequestParameterSupported bool `json:"request_parameter_supported"`
59 RequestUriParameterSupported bool `json:"request_uri_parameter_supported"`
60 RequireRequestUriRegistration *bool `json:"require_request_uri_registration,omitempty"`
61 ScopesSupported []string `json:"scopes_supported"`
62 SubjectTypesSupported []string `json:"subject_types_supported"`
63 ResponseTypesSupported []string `json:"response_types_supported"`
64 ResponseModesSupported []string `json:"response_modes_supported"`
65 GrantTypesSupported []string `json:"grant_types_supported"`
66 CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
67 UILocalesSupported []string `json:"ui_locales_supported"`
68 DisplayValuesSupported []string `json:"display_values_supported"`
69 RequestObjectSigningAlgValuesSupported []string `json:"request_object_signing_alg_values_supported"`
70 AuthorizationResponseISSParameterSupported bool `json:"authorization_response_iss_parameter_supported"`
71 RequestObjectEncryptionAlgValuesSupported []string `json:"request_object_encryption_alg_values_supported"`
72 RequestObjectEncryptionEncValuesSupported []string `json:"request_object_encryption_enc_values_supported"`
73 JwksUri string `json:"jwks_uri"`
74 AuthorizationEndpoint string `json:"authorization_endpoint"`
75 TokenEndpoint string `json:"token_endpoint"`
76 TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported"`
77 TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported"`
78 RevocationEndpoint string `json:"revocation_endpoint"`
79 IntrospectionEndpoint string `json:"introspection_endpoint"`
80 PushedAuthorizationRequestEndpoint string `json:"pushed_authorization_request_endpoint"`
81 RequirePushedAuthorizationRequests bool `json:"require_pushed_authorization_requests"`
82 DpopSigningAlgValuesSupported []string `json:"dpop_signing_alg_values_supported"`
83 ProtectedResources []string `json:"protected_resources"`
84 ClientIDMetadataDocumentSupported bool `json:"client_id_metadata_document_supported"`
85}
86
87func (oam *OauthAuthorizationMetadata) UnmarshalJSON(b []byte) error {
88 type Tmp OauthAuthorizationMetadata
89 var tmp Tmp
90
91 if err := json.Unmarshal(b, &tmp); err != nil {
92 return err
93 }
94
95 *oam = OauthAuthorizationMetadata(tmp)
96
97 return nil
98}
99
100func (oam *OauthAuthorizationMetadata) Validate(fetch_url *url.URL, insecure bool) error {
101 if fetch_url == nil {
102 return fmt.Errorf("fetch_url was nil")
103 }
104
105 iu, err := url.Parse(oam.Issuer)
106 if err != nil {
107 return err
108 }
109
110 if iu.Hostname() != fetch_url.Hostname() {
111 return fmt.Errorf("issuer hostname does not match fetch url hostname")
112 }
113
114 if !insecure {
115 if iu.Scheme != "https" {
116 return fmt.Errorf("issuer url is not https")
117 }
118
119 if iu.Port() != "" {
120 return fmt.Errorf("issuer port is not empty")
121 }
122 }
123
124 if iu.Path != "" && iu.Path != "/" {
125 return fmt.Errorf("issuer path is not /")
126 }
127
128 if iu.RawQuery != "" {
129 return fmt.Errorf("issuer url params are not empty")
130 }
131
132 if !slices.Contains(oam.ResponseTypesSupported, "code") {
133 return fmt.Errorf("`code` is not in response_types_supported")
134 }
135
136 if !slices.Contains(oam.GrantTypesSupported, "authorization_code") {
137 return fmt.Errorf("`authorization_code` is not in grant_types_supported")
138 }
139
140 if !slices.Contains(oam.GrantTypesSupported, "refresh_token") {
141 return fmt.Errorf("`refresh_token` is not in grant_types_supported")
142 }
143
144 if !slices.Contains(oam.CodeChallengeMethodsSupported, "S256") {
145 return fmt.Errorf("`S256` is not in code_challenge_methods_supported")
146 }
147
148 if !slices.Contains(oam.TokenEndpointAuthMethodsSupported, "none") {
149 return fmt.Errorf("`none` is not in token_endpoint_auth_methods_supported")
150 }
151
152 if !slices.Contains(oam.TokenEndpointAuthMethodsSupported, "private_key_jwt") {
153 return fmt.Errorf("`private_key_jwt` is not in token_endpoint_auth_methods_supported")
154 }
155
156 if !slices.Contains(oam.TokenEndpointAuthSigningAlgValuesSupported, "ES256") {
157 return fmt.Errorf("`ES256` is not in token_endpoint_auth_signing_alg_values_supported")
158 }
159
160 if !slices.Contains(oam.ScopesSupported, "atproto") {
161 return fmt.Errorf("`atproto` is not in scopes_supported")
162 }
163
164 if oam.AuthorizationResponseISSParameterSupported != true {
165 return fmt.Errorf("authorization_response_iss_parameter_supported is not true")
166 }
167
168 if oam.PushedAuthorizationRequestEndpoint == "" {
169 return fmt.Errorf("pushed_authorization_request_endpoint is empty")
170 }
171
172 if oam.RequirePushedAuthorizationRequests == false {
173 return fmt.Errorf("require_pushed_authorization_requests is false")
174 }
175
176 if !slices.Contains(oam.DpopSigningAlgValuesSupported, "ES256") {
177 return fmt.Errorf("`ES256` is not in dpop_signing_alg_values_supported")
178 }
179
180 if oam.RequireRequestUriRegistration != nil && *oam.RequireRequestUriRegistration == false {
181 return fmt.Errorf("require_request_uri_registration present in metadata and was false")
182 }
183
184 if oam.ClientIDMetadataDocumentSupported == false {
185 return fmt.Errorf("client_id_metadata_document_supported was false")
186 }
187
188 return nil
189}