@@ -15,6 +15,7 @@ limitations under the License.
15
15
*/
16
16
17
17
import type { RequestFunction } from "../../platform/types/types" ;
18
+ import type { URLRouter } from "../../domain/navigation/URLRouter.js" ;
18
19
19
20
const WELL_KNOWN = ".well-known/openid-configuration" ;
20
21
@@ -41,6 +42,7 @@ const isValidBearerToken = (t: any): t is BearerToken =>
41
42
type AuthorizationParams = {
42
43
state : string ,
43
44
scope : string ,
45
+ clientId : string ,
44
46
redirectUri : string ,
45
47
nonce ?: string ,
46
48
codeVerifier ?: string ,
@@ -54,18 +56,35 @@ function assert(condition: any, message: string): asserts condition {
54
56
55
57
export class OidcApi {
56
58
_issuer : string ;
57
- _clientId : string ;
58
59
_requestFn : RequestFunction ;
59
60
_encoding : any ;
60
61
_crypto : any ;
62
+ _urlCreator : URLRouter ;
61
63
_metadataPromise : Promise < any > ;
64
+ _registrationPromise : Promise < any > ;
62
65
63
- constructor ( { issuer, clientId , request, encoding, crypto } ) {
66
+ constructor ( { issuer, request, encoding, crypto, urlCreator , clientId } ) {
64
67
this . _issuer = issuer ;
65
- this . _clientId = clientId ;
66
68
this . _requestFn = request ;
67
69
this . _encoding = encoding ;
68
70
this . _crypto = crypto ;
71
+ this . _urlCreator = urlCreator ;
72
+
73
+ if ( clientId ) {
74
+ this . _registrationPromise = Promise . resolve ( { client_id : clientId } ) ;
75
+ }
76
+ }
77
+
78
+ get clientMetadata ( ) {
79
+ return {
80
+ client_name : "Hydrogen Web" ,
81
+ logo_uri : this . _urlCreator . absoluteUrlForAsset ( "icon.png" ) ,
82
+ response_types : [ "code" ] ,
83
+ grant_types : [ "authorization_code" , "refresh_token" ] ,
84
+ redirect_uris : [ this . _urlCreator . createOIDCRedirectURL ( ) ] ,
85
+ id_token_signed_response_alg : "RS256" ,
86
+ token_endpoint_auth_method : "none" ,
87
+ } ;
69
88
}
70
89
71
90
get metadataUrl ( ) {
@@ -76,11 +95,35 @@ export class OidcApi {
76
95
return this . _issuer ;
77
96
}
78
97
79
- get redirectUri ( ) {
80
- return window . location . origin ;
98
+ async clientId ( ) : Promise < string > {
99
+ return ( await this . registration ( ) ) [ "client_id" ] ;
100
+ }
101
+
102
+ registration ( ) : Promise < any > {
103
+ if ( ! this . _registrationPromise ) {
104
+ this . _registrationPromise = ( async ( ) => {
105
+ const headers = new Map ( ) ;
106
+ headers . set ( "Accept" , "application/json" ) ;
107
+ headers . set ( "Content-Type" , "application/json" ) ;
108
+ const req = this . _requestFn ( await this . registrationEndpoint ( ) , {
109
+ method : "POST" ,
110
+ headers,
111
+ format : "json" ,
112
+ body : JSON . stringify ( this . clientMetadata ) ,
113
+ } ) ;
114
+ const res = await req . response ( ) ;
115
+ if ( res . status >= 400 ) {
116
+ throw new Error ( "failed to register client" ) ;
117
+ }
118
+
119
+ return res . body ;
120
+ } ) ( ) ;
121
+ }
122
+
123
+ return this . _registrationPromise ;
81
124
}
82
125
83
- metadata ( ) {
126
+ metadata ( ) : Promise < any > {
84
127
if ( ! this . _metadataPromise ) {
85
128
this . _metadataPromise = ( async ( ) => {
86
129
const headers = new Map ( ) ;
@@ -105,6 +148,7 @@ export class OidcApi {
105
148
const m = await this . metadata ( ) ;
106
149
assert ( typeof m . authorization_endpoint === "string" , "Has an authorization endpoint" ) ;
107
150
assert ( typeof m . token_endpoint === "string" , "Has a token endpoint" ) ;
151
+ assert ( typeof m . registration_endpoint === "string" , "Has a registration endpoint" ) ;
108
152
assert ( Array . isArray ( m . response_types_supported ) && m . response_types_supported . includes ( "code" ) , "Supports the code response type" ) ;
109
153
assert ( Array . isArray ( m . response_modes_supported ) && m . response_modes_supported . includes ( "fragment" ) , "Supports the fragment response mode" ) ;
110
154
assert ( Array . isArray ( m . grant_types_supported ) && m . grant_types_supported . includes ( "authorization_code" ) , "Supports the authorization_code grant type" ) ;
@@ -126,13 +170,13 @@ export class OidcApi {
126
170
scope,
127
171
nonce,
128
172
codeVerifier,
129
- } : AuthorizationParams ) {
173
+ } : AuthorizationParams ) : Promise < string > {
130
174
const metadata = await this . metadata ( ) ;
131
175
const url = new URL ( metadata [ "authorization_endpoint" ] ) ;
132
176
url . searchParams . append ( "response_mode" , "fragment" ) ;
133
177
url . searchParams . append ( "response_type" , "code" ) ;
134
178
url . searchParams . append ( "redirect_uri" , redirectUri ) ;
135
- url . searchParams . append ( "client_id" , this . _clientId ) ;
179
+ url . searchParams . append ( "client_id" , await this . clientId ( ) ) ;
136
180
url . searchParams . append ( "state" , state ) ;
137
181
url . searchParams . append ( "scope" , scope ) ;
138
182
if ( nonce ) {
@@ -147,11 +191,16 @@ export class OidcApi {
147
191
return url . toString ( ) ;
148
192
}
149
193
150
- async tokenEndpoint ( ) {
194
+ async tokenEndpoint ( ) : Promise < string > {
151
195
const metadata = await this . metadata ( ) ;
152
196
return metadata [ "token_endpoint" ] ;
153
197
}
154
198
199
+ async registrationEndpoint ( ) : Promise < string > {
200
+ const metadata = await this . metadata ( ) ;
201
+ return metadata [ "registration_endpoint" ] ;
202
+ }
203
+
155
204
generateParams ( { scope, redirectUri } : { scope : string , redirectUri : string } ) : AuthorizationParams {
156
205
return {
157
206
scope,
@@ -169,7 +218,7 @@ export class OidcApi {
169
218
} : { codeVerifier : string , code : string , redirectUri : string } ) : Promise < BearerToken > {
170
219
const params = new URLSearchParams ( ) ;
171
220
params . append ( "grant_type" , "authorization_code" ) ;
172
- params . append ( "client_id" , this . _clientId ) ;
221
+ params . append ( "client_id" , await this . clientId ( ) ) ;
173
222
params . append ( "code_verifier" , codeVerifier ) ;
174
223
params . append ( "redirect_uri" , redirectUri ) ;
175
224
params . append ( "code" , code ) ;
@@ -201,7 +250,7 @@ export class OidcApi {
201
250
} : { refreshToken : string } ) : Promise < BearerToken > {
202
251
const params = new URLSearchParams ( ) ;
203
252
params . append ( "grant_type" , "refresh_token" ) ;
204
- params . append ( "client_id" , this . _clientId ) ;
253
+ params . append ( "client_id" , await this . clientId ( ) ) ;
205
254
params . append ( "refresh_token" , refreshToken ) ;
206
255
const body = params . toString ( ) ;
207
256
0 commit comments