token_authority 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +199 -7
- data/app/controllers/concerns/token_authority/client_authentication.rb +141 -0
- data/app/controllers/concerns/token_authority/controller_event_logging.rb +98 -0
- data/app/controllers/concerns/token_authority/initial_access_token_authentication.rb +35 -0
- data/app/controllers/concerns/token_authority/token_authentication.rb +128 -0
- data/app/controllers/token_authority/authorization_grants_controller.rb +119 -0
- data/app/controllers/token_authority/authorizations_controller.rb +105 -0
- data/app/controllers/token_authority/clients_controller.rb +99 -0
- data/app/controllers/token_authority/metadata_controller.rb +12 -0
- data/app/controllers/token_authority/resource_metadata_controller.rb +12 -0
- data/app/controllers/token_authority/sessions_controller.rb +228 -0
- data/app/helpers/token_authority/authorization_grants_helper.rb +27 -0
- data/app/models/concerns/token_authority/claim_validatable.rb +95 -0
- data/app/models/concerns/token_authority/event_logging.rb +144 -0
- data/app/models/concerns/token_authority/resourceable.rb +111 -0
- data/app/models/concerns/token_authority/scopeable.rb +105 -0
- data/app/models/concerns/token_authority/session_creatable.rb +101 -0
- data/app/models/token_authority/access_token.rb +127 -0
- data/app/models/token_authority/access_token_request.rb +193 -0
- data/app/models/token_authority/authorization_grant.rb +119 -0
- data/app/models/token_authority/authorization_request.rb +276 -0
- data/app/models/token_authority/authorization_server_metadata.rb +101 -0
- data/app/models/token_authority/client.rb +263 -0
- data/app/models/token_authority/client_id_resolver.rb +114 -0
- data/app/models/token_authority/client_metadata_document.rb +164 -0
- data/app/models/token_authority/client_metadata_document_cache.rb +33 -0
- data/app/models/token_authority/client_metadata_document_fetcher.rb +266 -0
- data/app/models/token_authority/client_registration_request.rb +214 -0
- data/app/models/token_authority/client_registration_response.rb +58 -0
- data/app/models/token_authority/jwks_cache.rb +37 -0
- data/app/models/token_authority/jwks_fetcher.rb +70 -0
- data/app/models/token_authority/protected_resource_metadata.rb +74 -0
- data/app/models/token_authority/refresh_token.rb +110 -0
- data/app/models/token_authority/refresh_token_request.rb +116 -0
- data/app/models/token_authority/session.rb +193 -0
- data/app/models/token_authority/software_statement.rb +70 -0
- data/app/views/token_authority/authorization_grants/new.html.erb +25 -0
- data/app/views/token_authority/client_error.html.erb +8 -0
- data/config/locales/token_authority.en.yml +248 -0
- data/config/routes.rb +29 -0
- data/lib/generators/token_authority/install/install_generator.rb +61 -0
- data/lib/generators/token_authority/install/templates/create_token_authority_tables.rb.erb +116 -0
- data/lib/generators/token_authority/install/templates/token_authority.rb +247 -0
- data/lib/token_authority/configuration.rb +397 -0
- data/lib/token_authority/engine.rb +34 -0
- data/lib/token_authority/errors.rb +221 -0
- data/lib/token_authority/instrumentation.rb +80 -0
- data/lib/token_authority/instrumentation_log_subscriber.rb +62 -0
- data/lib/token_authority/json_web_token.rb +78 -0
- data/lib/token_authority/log_event_subscriber.rb +43 -0
- data/lib/token_authority/routing/constraints.rb +71 -0
- data/lib/token_authority/routing/routes.rb +39 -0
- data/lib/token_authority/version.rb +4 -1
- data/lib/token_authority.rb +30 -1
- metadata +65 -5
- data/app/assets/stylesheets/token_authority/application.css +0 -15
- data/app/controllers/token_authority/application_controller.rb +0 -4
- data/app/helpers/token_authority/application_helper.rb +0 -4
- data/app/views/layouts/token_authority/application.html.erb +0 -17
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
en:
|
|
2
|
+
activerecord:
|
|
3
|
+
models:
|
|
4
|
+
token_authority/authorization_grant: 'Authorization grant'
|
|
5
|
+
token_authority/challenge: 'Challenge'
|
|
6
|
+
token_authority/client: 'Client'
|
|
7
|
+
token_authority/session: 'Session'
|
|
8
|
+
attributes:
|
|
9
|
+
token_authority/authorization_grant:
|
|
10
|
+
token_authority_client_id: 'TokenAuthority client ID'
|
|
11
|
+
client_id_url: 'Client ID URL'
|
|
12
|
+
redeemed: 'Redeemed'
|
|
13
|
+
user_id: 'User ID'
|
|
14
|
+
token_authority/challenge:
|
|
15
|
+
code_challenge: 'Code challenge'
|
|
16
|
+
code_challenge_method: 'Code challenge method'
|
|
17
|
+
token_authority_authorization_grant_id: 'TokenAuthority authorization grant ID'
|
|
18
|
+
redirect_uri: 'Redirect URI'
|
|
19
|
+
token_authority/client:
|
|
20
|
+
access_token_duration: 'Access token duration'
|
|
21
|
+
client_secret_id: 'Client secret ID'
|
|
22
|
+
client_type: 'Client type'
|
|
23
|
+
name: 'Name'
|
|
24
|
+
redirect_uris: 'Redirect URIs'
|
|
25
|
+
refresh_token_duration: 'Refresh token duration'
|
|
26
|
+
token_endpoint_auth_method: 'Token endpoint auth method'
|
|
27
|
+
grant_types: 'Grant types'
|
|
28
|
+
response_types: 'Response types'
|
|
29
|
+
scope: 'Scope'
|
|
30
|
+
client_uri: 'Client URI'
|
|
31
|
+
logo_uri: 'Logo URI'
|
|
32
|
+
tos_uri: 'Terms of service URI'
|
|
33
|
+
policy_uri: 'Policy URI'
|
|
34
|
+
contacts: 'Contacts'
|
|
35
|
+
jwks_uri: 'JWKS URI'
|
|
36
|
+
jwks: 'JWKS'
|
|
37
|
+
software_id: 'Software ID'
|
|
38
|
+
software_version: 'Software version'
|
|
39
|
+
software_statement: 'Software statement'
|
|
40
|
+
client_id_issued_at: 'Client ID issued at'
|
|
41
|
+
client_secret_expires_at: 'Client secret expires at'
|
|
42
|
+
dynamically_registered: 'Dynamically registered'
|
|
43
|
+
token_authority/session:
|
|
44
|
+
access_token_jti: 'Access token JTI'
|
|
45
|
+
client_id: 'Client ID'
|
|
46
|
+
expires_at: 'Expires at'
|
|
47
|
+
token_authority_authorization_grant_id: 'TokenAuthority authorization grant ID'
|
|
48
|
+
status: 'Status'
|
|
49
|
+
refresh_token_jti: 'Refresh token JTI'
|
|
50
|
+
user_id: 'User ID'
|
|
51
|
+
errors:
|
|
52
|
+
models:
|
|
53
|
+
token_authority/authorization_grant:
|
|
54
|
+
attributes:
|
|
55
|
+
token_authority_client_id:
|
|
56
|
+
blank: 'must be linked to a client'
|
|
57
|
+
user_id:
|
|
58
|
+
blank: 'must be linked to a user'
|
|
59
|
+
base:
|
|
60
|
+
must_have_client_identifier: 'must have either a registered client or a client ID URL'
|
|
61
|
+
token_authority/challenge:
|
|
62
|
+
attributes:
|
|
63
|
+
code_challenge:
|
|
64
|
+
blank: 'is required for PKCE'
|
|
65
|
+
failed_challenge: 'failed validation'
|
|
66
|
+
requires_code_verifier: 'must be validated against a code_verifier'
|
|
67
|
+
code_challenge_method:
|
|
68
|
+
blank: 'is required for PKCE'
|
|
69
|
+
invalid: 'must be a supported code challenge method'
|
|
70
|
+
token_authority_authorization_grant_id:
|
|
71
|
+
blank: 'must be linked to an authorization grant'
|
|
72
|
+
redirect_uri:
|
|
73
|
+
blank: 'must be provided if sent in the original authorization request'
|
|
74
|
+
invalid: 'must be a supported code challenge method'
|
|
75
|
+
token_authority/client:
|
|
76
|
+
attributes:
|
|
77
|
+
redirect_uris:
|
|
78
|
+
invalid_http_scheme: 'must contain valid HTTP(S) scheme in all URIs'
|
|
79
|
+
invalid_uri: 'must contain valid URIs'
|
|
80
|
+
base:
|
|
81
|
+
jwks_required_for_private_key_jwt: 'JWKS or JWKS URI is required when using private_key_jwt authentication'
|
|
82
|
+
token_authority/session:
|
|
83
|
+
access_token_jti:
|
|
84
|
+
invalid: 'must be a valid UUID'
|
|
85
|
+
client_id:
|
|
86
|
+
blank: 'must be linked to a client'
|
|
87
|
+
token_authority_authorization_grant_id:
|
|
88
|
+
blank: 'must be linked to an authorization grant'
|
|
89
|
+
refresh_token_jti:
|
|
90
|
+
invalid: 'must be a valid UUID'
|
|
91
|
+
user_id:
|
|
92
|
+
blank: 'must be linked to a user'
|
|
93
|
+
activemodel:
|
|
94
|
+
models:
|
|
95
|
+
token_authority/access_token: 'Access token'
|
|
96
|
+
token_authority/access_token_request: 'Access token request'
|
|
97
|
+
token_authority/authorization_request: 'Authorization request'
|
|
98
|
+
token_authority/refresh_token: 'Refresh token'
|
|
99
|
+
attributes:
|
|
100
|
+
token_authority/access_token:
|
|
101
|
+
aud: 'Audience (aud)'
|
|
102
|
+
exp: 'Expires at (exp)'
|
|
103
|
+
iat: 'Issued at (iat)'
|
|
104
|
+
iss: 'Issuer (iss)'
|
|
105
|
+
jti: 'JTI (jti)'
|
|
106
|
+
user_id: 'User ID'
|
|
107
|
+
token_authority/access_token_request:
|
|
108
|
+
code_verifier: 'Code verifier'
|
|
109
|
+
token_authority_authorization_grant: 'TokenAuthority authorization grant'
|
|
110
|
+
redirect_uri: 'Redirect URI'
|
|
111
|
+
token_authority/authorization_request:
|
|
112
|
+
client_id: 'Client ID'
|
|
113
|
+
code_challenge: 'Code challenge'
|
|
114
|
+
code_challenge_method: 'Code challenge method'
|
|
115
|
+
token_authority_client: 'TokenAuthority client'
|
|
116
|
+
redirect_uri: 'Redirect URI'
|
|
117
|
+
response_type: 'Response type'
|
|
118
|
+
state: 'State'
|
|
119
|
+
resources: 'Resources'
|
|
120
|
+
scope: 'Scope'
|
|
121
|
+
token_authority/access_token_request:
|
|
122
|
+
resources: 'Resources'
|
|
123
|
+
scope: 'Scope'
|
|
124
|
+
token_authority/refresh_token:
|
|
125
|
+
aud: 'Audience (aud)'
|
|
126
|
+
exp: 'Expires at (exp)'
|
|
127
|
+
iat: 'Issued at (iat)'
|
|
128
|
+
iss: 'Issuer (iss)'
|
|
129
|
+
jti: 'JTI (jti)'
|
|
130
|
+
token_authority/refresh_token_request:
|
|
131
|
+
token: 'Token'
|
|
132
|
+
resources: 'Resources'
|
|
133
|
+
client_id: 'Client ID'
|
|
134
|
+
scope: 'Scope'
|
|
135
|
+
errors:
|
|
136
|
+
models:
|
|
137
|
+
token_authority/access_token:
|
|
138
|
+
attributes:
|
|
139
|
+
aud:
|
|
140
|
+
blank: 'claim not present in token'
|
|
141
|
+
invalid: 'claim does not match server configuration'
|
|
142
|
+
exp:
|
|
143
|
+
blank: 'claim not present in token'
|
|
144
|
+
expired: 'is in the past'
|
|
145
|
+
iss:
|
|
146
|
+
blank: 'claim not present in token'
|
|
147
|
+
invalid: 'claim does not match server configuration'
|
|
148
|
+
jti:
|
|
149
|
+
blank: 'claim not present in token'
|
|
150
|
+
user_id:
|
|
151
|
+
blank: 'claim not present in token'
|
|
152
|
+
token_authority/access_token_request:
|
|
153
|
+
attributes:
|
|
154
|
+
code_verifier:
|
|
155
|
+
blank: 'must be provided as a param in the access token request'
|
|
156
|
+
does_not_validate_code_challenge: 'does not vaidate code_challenge from the authorize request'
|
|
157
|
+
present_in_authorize: 'must be provided if sent in the original authorization request'
|
|
158
|
+
token_authority_authorization_grant:
|
|
159
|
+
invalid: 'must be initialized with an authorization grant'
|
|
160
|
+
redirect_uri:
|
|
161
|
+
blank: 'must be provided as a param in the access token request'
|
|
162
|
+
mismatched: 'does not match the redirect_uri from the authorize request'
|
|
163
|
+
present_in_authorize: 'must be provided if sent in the original authorization request'
|
|
164
|
+
resources:
|
|
165
|
+
invalid_uri: 'must contain valid absolute URIs with http/https scheme and no fragments'
|
|
166
|
+
not_allowed: 'must only contain allowed resource URIs'
|
|
167
|
+
not_subset: 'must be a subset of the originally granted resources'
|
|
168
|
+
scope:
|
|
169
|
+
invalid: 'contains invalid scope tokens'
|
|
170
|
+
not_allowed: 'must only contain allowed scopes'
|
|
171
|
+
not_subset: 'must be a subset of the originally granted scopes'
|
|
172
|
+
token_authority/authorization_request:
|
|
173
|
+
attributes:
|
|
174
|
+
client_id:
|
|
175
|
+
blank: 'must be provided as a param in the request to authorize when client is public'
|
|
176
|
+
mismatched: 'does not match the client ID URL'
|
|
177
|
+
unregistered_client: 'does not map to a registered client'
|
|
178
|
+
code_challenge:
|
|
179
|
+
required_if_other_pkce_params_present: 'must be provided as a param in the request to authorize if other PKCE params were sent'
|
|
180
|
+
required_for_public_clients: 'must be provided as a param in the request to authorize when client is public'
|
|
181
|
+
code_challenge_method:
|
|
182
|
+
invalid: 'must be a supported code challenge method'
|
|
183
|
+
required_if_other_pkce_params_present: 'must be provided as a param in the request to authorize if other PKCE params were sent'
|
|
184
|
+
required_for_public_clients: 'must be provided as a param in the request to authorize when client is public'
|
|
185
|
+
token_authority_client:
|
|
186
|
+
invalid: 'must be initiated by a client'
|
|
187
|
+
redirect_uri:
|
|
188
|
+
blank: 'must be provided as a param in the request to authorize when client is public'
|
|
189
|
+
invalid: 'must match redirect_uri configured for client'
|
|
190
|
+
response_type:
|
|
191
|
+
blank: 'must be provided as a param in the request to authorize'
|
|
192
|
+
invalid: 'must be a supported response type'
|
|
193
|
+
state:
|
|
194
|
+
resources:
|
|
195
|
+
required: 'must be provided when resource indicators are required'
|
|
196
|
+
invalid_uri: 'must contain valid absolute URIs with http/https scheme and no fragments'
|
|
197
|
+
not_allowed: 'must only contain allowed resource URIs'
|
|
198
|
+
not_subset: 'must be a subset of the originally granted resources'
|
|
199
|
+
scope:
|
|
200
|
+
required: 'must be provided when scopes are required'
|
|
201
|
+
invalid: 'contains invalid scope tokens'
|
|
202
|
+
not_allowed: 'must only contain allowed scopes'
|
|
203
|
+
token_authority/refresh_token:
|
|
204
|
+
attributes:
|
|
205
|
+
aud:
|
|
206
|
+
blank: 'claim not present in token'
|
|
207
|
+
invalid: 'claim does not match server configuration'
|
|
208
|
+
exp:
|
|
209
|
+
blank: 'claim not present in token'
|
|
210
|
+
expired: 'is in the past'
|
|
211
|
+
iss:
|
|
212
|
+
blank: 'claim not present in token'
|
|
213
|
+
invalid: 'claim does not match server configuration'
|
|
214
|
+
jti:
|
|
215
|
+
blank: 'claim not present in token'
|
|
216
|
+
token_authority/refresh_token_request:
|
|
217
|
+
attributes:
|
|
218
|
+
token:
|
|
219
|
+
blank: 'must be provided as a param in the refresh token request'
|
|
220
|
+
session_not_found: 'does not map to a valid session'
|
|
221
|
+
resources:
|
|
222
|
+
invalid_uri: 'must contain valid absolute URIs with http/https scheme and no fragments'
|
|
223
|
+
not_allowed: 'must only contain allowed resource URIs'
|
|
224
|
+
not_subset: 'must be a subset of the originally granted resources'
|
|
225
|
+
scope:
|
|
226
|
+
invalid: 'contains invalid scope tokens'
|
|
227
|
+
not_allowed: 'must only contain allowed scopes'
|
|
228
|
+
not_subset: 'must be a subset of the originally granted scopes'
|
|
229
|
+
token_authority:
|
|
230
|
+
authorization_grants:
|
|
231
|
+
new:
|
|
232
|
+
title: 'Authorize this Client'
|
|
233
|
+
lede: 'Confirm that you wish to grant access to this client.'
|
|
234
|
+
resources_heading: 'This application is requesting access to:'
|
|
235
|
+
scopes_heading: 'This application is requesting the following permissions:'
|
|
236
|
+
approve_cta: 'Approve'
|
|
237
|
+
reject_cta: 'Reject'
|
|
238
|
+
client_error:
|
|
239
|
+
title: 'Client Error'
|
|
240
|
+
lede: 'The request provided by the client to this server is malformed. Please report this error through the client support channel.'
|
|
241
|
+
errors:
|
|
242
|
+
invalid_grant: 'The authorization grant is invalid'
|
|
243
|
+
mismatched_refresh_token: 'The provided refresh token JTI does not match the refresh token JTI of the target OAuthSession.'
|
|
244
|
+
oauth_session_failure: 'Failed to create OAuthSession. Errors: %{errors}'
|
|
245
|
+
revoked_session: 'Refresh token replay attack detected. Refreshed OAuthSession: %{refreshed_session_id}, Revoked OAuthSession: %{revoked_session_id}, Client ID: %{client_id}, User ID: %{user_id}'
|
|
246
|
+
missing_auth_header: 'Authorization header is missing or empty'
|
|
247
|
+
invalid_token: 'The access token is invalid or malformed'
|
|
248
|
+
unauthorized_token: 'The access token is expired or unauthorized'
|
data/config/routes.rb
CHANGED
|
@@ -1,2 +1,31 @@
|
|
|
1
1
|
TokenAuthority::Engine.routes.draw do
|
|
2
|
+
get "authorize", to: "authorizations#authorize"
|
|
3
|
+
resources :authorization_grants, path: "authorization-grants", only: %i[new create]
|
|
4
|
+
|
|
5
|
+
# Dynamic Client Registration (RFC 7591)
|
|
6
|
+
constraints(TokenAuthority::Routing::DynamicRegistrationEnabledConstraint.new) do
|
|
7
|
+
post "register", to: "clients#create"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Token endpoint with grant_type constraints
|
|
11
|
+
constraints(TokenAuthority::Routing::GrantTypeConstraint.new("refresh_token")) do
|
|
12
|
+
post "token", to: "sessions#refresh", as: :refresh_session
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
constraints(TokenAuthority::Routing::GrantTypeConstraint.new("authorization_code")) do
|
|
16
|
+
post "token", to: "sessions#token", as: :create_session
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
post "token", to: "sessions#unsupported_grant_type", as: :unsupported_grant_type
|
|
20
|
+
|
|
21
|
+
# Revoke endpoint with token_type_hint constraints
|
|
22
|
+
constraints(TokenAuthority::Routing::TokenTypeHintConstraint.new("access_token")) do
|
|
23
|
+
post "revoke", to: "sessions#revoke_access_token", as: :revoke_access_token
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
constraints(TokenAuthority::Routing::TokenTypeHintConstraint.new("refresh_token")) do
|
|
27
|
+
post "revoke", to: "sessions#revoke_refresh_token", as: :revoke_refresh_token
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
post "revoke", to: "sessions#revoke"
|
|
2
31
|
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module TokenAuthority
|
|
7
|
+
module Generators
|
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
|
9
|
+
include ActiveRecord::Generators::Migration
|
|
10
|
+
|
|
11
|
+
source_root File.expand_path("templates", __dir__)
|
|
12
|
+
|
|
13
|
+
class_option :user_table_name,
|
|
14
|
+
type: :string,
|
|
15
|
+
default: "users",
|
|
16
|
+
desc: "Name of the user table in your application"
|
|
17
|
+
|
|
18
|
+
class_option :user_foreign_key_type,
|
|
19
|
+
type: :string,
|
|
20
|
+
default: "bigint",
|
|
21
|
+
desc: "Type of the user table's primary key (bigint, uuid, integer)"
|
|
22
|
+
|
|
23
|
+
def create_migration_file
|
|
24
|
+
migration_template(
|
|
25
|
+
"create_token_authority_tables.rb.erb",
|
|
26
|
+
"db/migrate/create_token_authority_tables.rb"
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_initializer_file
|
|
31
|
+
template "token_authority.rb", "config/initializers/token_authority.rb"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def copy_views
|
|
35
|
+
directory engine_views_path, "app/views/token_authority"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def add_routes
|
|
39
|
+
route "token_authority_routes"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def engine_views_path
|
|
45
|
+
File.expand_path("../../../../app/views/token_authority", __dir__)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def user_table_name
|
|
49
|
+
options[:user_table_name]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def user_foreign_key_type
|
|
53
|
+
options[:user_foreign_key_type]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def migration_version
|
|
57
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateTokenAuthorityTables < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
# OAuth Clients - registered applications that can request tokens
|
|
6
|
+
create_table :token_authority_clients do |t|
|
|
7
|
+
t.string :public_id, null: false # Public-facing identifier (UUID)
|
|
8
|
+
t.string :name, null: false
|
|
9
|
+
t.string :client_type, null: false, default: "confidential" # "confidential" or "public"
|
|
10
|
+
t.json :redirect_uris, null: false # Array of redirect URIs
|
|
11
|
+
t.bigint :access_token_duration, null: false
|
|
12
|
+
t.bigint :refresh_token_duration, null: false
|
|
13
|
+
t.string :client_secret_id # Used for HMAC-based secret generation (confidential clients only)
|
|
14
|
+
|
|
15
|
+
# RFC 7591 client metadata
|
|
16
|
+
t.string :token_endpoint_auth_method, null: false, default: "client_secret_basic"
|
|
17
|
+
t.json :grant_types # Array, default: ["authorization_code"]
|
|
18
|
+
t.json :response_types # Array, default: ["code"]
|
|
19
|
+
t.string :scope # space-delimited scope string
|
|
20
|
+
|
|
21
|
+
# Human-readable metadata
|
|
22
|
+
t.string :client_uri
|
|
23
|
+
t.string :logo_uri
|
|
24
|
+
t.string :tos_uri
|
|
25
|
+
t.string :policy_uri
|
|
26
|
+
t.json :contacts # Array of email addresses
|
|
27
|
+
|
|
28
|
+
# Technical metadata (for JWT auth methods)
|
|
29
|
+
t.string :jwks_uri
|
|
30
|
+
t.json :jwks # JWKS object (inline)
|
|
31
|
+
t.string :software_id
|
|
32
|
+
t.string :software_version
|
|
33
|
+
t.text :software_statement # Original software statement JWT (if provided)
|
|
34
|
+
|
|
35
|
+
# Registration metadata
|
|
36
|
+
t.datetime :client_id_issued_at
|
|
37
|
+
t.datetime :client_secret_expires_at
|
|
38
|
+
t.boolean :dynamically_registered, null: false, default: false
|
|
39
|
+
|
|
40
|
+
t.timestamps
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
add_index :token_authority_clients, :public_id, unique: true
|
|
44
|
+
add_index :token_authority_clients, :client_secret_id, unique: true
|
|
45
|
+
add_index :token_authority_clients, :software_id
|
|
46
|
+
|
|
47
|
+
# Authorization Grants - authorization codes issued during OAuth flow
|
|
48
|
+
create_table :token_authority_authorization_grants do |t|
|
|
49
|
+
t.string :public_id, null: false # Public-facing identifier (UUID) - this is the authorization code
|
|
50
|
+
t.datetime :expires_at, null: false
|
|
51
|
+
t.boolean :redeemed, null: false, default: false
|
|
52
|
+
t.references :token_authority_client,
|
|
53
|
+
null: true, # Nullable for URL-based clients (Client Metadata Document)
|
|
54
|
+
foreign_key: { to_table: :token_authority_clients },
|
|
55
|
+
index: { name: "index_ta_auth_grants_on_client_id" }
|
|
56
|
+
t.string :client_id_url # URL for URL-based client IDs (Client Metadata Document)
|
|
57
|
+
t.<%= user_foreign_key_type %> :user_id
|
|
58
|
+
|
|
59
|
+
# PKCE parameters
|
|
60
|
+
t.string :code_challenge
|
|
61
|
+
t.string :code_challenge_method, default: "S256"
|
|
62
|
+
t.string :redirect_uri
|
|
63
|
+
t.json :resources, null: false, default: [] # RFC 8707 resource indicators
|
|
64
|
+
t.json :scopes, null: false, default: [] # OAuth scopes (RFC 6749 Section 3.3)
|
|
65
|
+
|
|
66
|
+
t.timestamps
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
add_index :token_authority_authorization_grants, :public_id, unique: true
|
|
70
|
+
add_index :token_authority_authorization_grants, :user_id, name: "index_ta_auth_grants_on_user_id"
|
|
71
|
+
add_index :token_authority_authorization_grants, :expires_at, name: "index_ta_auth_grants_on_expires_at"
|
|
72
|
+
add_foreign_key :token_authority_authorization_grants, :<%= user_table_name %>, column: :user_id
|
|
73
|
+
|
|
74
|
+
# OAuth Sessions - tracks issued tokens and their status
|
|
75
|
+
create_table :token_authority_sessions do |t|
|
|
76
|
+
t.string :access_token_jti, null: false
|
|
77
|
+
t.string :refresh_token_jti, null: false
|
|
78
|
+
t.string :status, null: false, default: "created" # "created", "expired", "refreshed", "revoked"
|
|
79
|
+
t.references :token_authority_authorization_grant,
|
|
80
|
+
null: true,
|
|
81
|
+
foreign_key: { to_table: :token_authority_authorization_grants },
|
|
82
|
+
index: { name: "index_ta_sessions_on_auth_grant_id" }
|
|
83
|
+
|
|
84
|
+
t.timestamps
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
add_index :token_authority_sessions, :access_token_jti, unique: true
|
|
88
|
+
add_index :token_authority_sessions, :refresh_token_jti, unique: true
|
|
89
|
+
|
|
90
|
+
# JWKS Cache - stores fetched JWKS from remote URIs
|
|
91
|
+
create_table :token_authority_jwks_caches do |t|
|
|
92
|
+
t.string :uri_hash, null: false # SHA256 hash of the URI for lookups
|
|
93
|
+
t.string :uri, null: false # Original URI for debugging/display
|
|
94
|
+
t.json :jwks, null: false # The cached JWKS data
|
|
95
|
+
t.datetime :expires_at, null: false # When this cache entry expires
|
|
96
|
+
|
|
97
|
+
t.timestamps
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
add_index :token_authority_jwks_caches, :uri_hash, unique: true
|
|
101
|
+
add_index :token_authority_jwks_caches, :expires_at
|
|
102
|
+
|
|
103
|
+
# Client Metadata Document Cache - stores fetched metadata documents
|
|
104
|
+
create_table :token_authority_client_metadata_document_caches do |t|
|
|
105
|
+
t.string :uri_hash, null: false # SHA256 hash of the URI for lookups
|
|
106
|
+
t.string :uri, null: false # Original URI for debugging/display
|
|
107
|
+
t.json :metadata, null: false # The cached metadata document
|
|
108
|
+
t.datetime :expires_at, null: false # When this cache entry expires
|
|
109
|
+
|
|
110
|
+
t.timestamps
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
add_index :token_authority_client_metadata_document_caches, :uri_hash, unique: true, name: "index_ta_client_metadata_caches_on_uri_hash"
|
|
114
|
+
add_index :token_authority_client_metadata_document_caches, :expires_at, name: "index_ta_client_metadata_caches_on_expires_at"
|
|
115
|
+
end
|
|
116
|
+
end
|