Generate the OIDC redirect URI from the URLRouter

This also saves the redirectUri during the flow
This commit is contained in:
Quentin Gliech 2022-03-03 15:16:43 +01:00
parent dac68f362a
commit d723561d66
No known key found for this signature in database
GPG Key ID: 22D62B84552719FC
5 changed files with 25 additions and 7 deletions

View File

@ -49,10 +49,11 @@ export class CompleteOIDCLoginViewModel extends ViewModel {
} }
const code = this._code; const code = this._code;
// TODO: cleanup settings storage // TODO: cleanup settings storage
const [startedAt, nonce, codeVerifier, homeserver, issuer] = await Promise.all([ const [startedAt, nonce, codeVerifier, redirectUri, homeserver, issuer] = await Promise.all([
this.platform.settingsStorage.getInt(`oidc_${this._state}_started_at`), this.platform.settingsStorage.getInt(`oidc_${this._state}_started_at`),
this.platform.settingsStorage.getString(`oidc_${this._state}_nonce`), this.platform.settingsStorage.getString(`oidc_${this._state}_nonce`),
this.platform.settingsStorage.getString(`oidc_${this._state}_code_verifier`), this.platform.settingsStorage.getString(`oidc_${this._state}_code_verifier`),
this.platform.settingsStorage.getString(`oidc_${this._state}_redirect_uri`),
this.platform.settingsStorage.getString(`oidc_${this._state}_homeserver`), this.platform.settingsStorage.getString(`oidc_${this._state}_homeserver`),
this.platform.settingsStorage.getString(`oidc_${this._state}_issuer`), this.platform.settingsStorage.getString(`oidc_${this._state}_issuer`),
]); ]);
@ -63,7 +64,7 @@ export class CompleteOIDCLoginViewModel extends ViewModel {
request: this._request, request: this._request,
encoding: this._encoding, encoding: this._encoding,
}); });
const method = new OIDCLoginMethod({oidcApi, nonce, codeVerifier, code, homeserver, startedAt}); const method = new OIDCLoginMethod({oidcApi, nonce, codeVerifier, code, homeserver, startedAt, redirectUri});
const status = await this._attemptLogin(method); const status = await this._attemptLogin(method);
let error = ""; let error = "";
switch (status) { switch (status) {

View File

@ -44,11 +44,15 @@ export class StartOIDCLoginViewModel extends ViewModel {
} }
async startOIDCLogin() { async startOIDCLogin() {
const p = this._api.generateParams("openid"); const p = this._api.generateParams({
scope: "openid",
redirectUri: this.urlCreator.createOIDCRedirectURL(),
});
await Promise.all([ await Promise.all([
this.platform.settingsStorage.setInt(`oidc_${p.state}_started_at`, Date.now()), this.platform.settingsStorage.setInt(`oidc_${p.state}_started_at`, Date.now()),
this.platform.settingsStorage.setString(`oidc_${p.state}_nonce`, p.nonce), this.platform.settingsStorage.setString(`oidc_${p.state}_nonce`, p.nonce),
this.platform.settingsStorage.setString(`oidc_${p.state}_code_verifier`, p.codeVerifier), this.platform.settingsStorage.setString(`oidc_${p.state}_code_verifier`, p.codeVerifier),
this.platform.settingsStorage.setString(`oidc_${p.state}_redirect_uri`, p.redirectUri),
this.platform.settingsStorage.setString(`oidc_${p.state}_homeserver`, this._homeserver), this.platform.settingsStorage.setString(`oidc_${p.state}_homeserver`, this._homeserver),
this.platform.settingsStorage.setString(`oidc_${p.state}_issuer`, this._issuer), this.platform.settingsStorage.setString(`oidc_${p.state}_issuer`, this._issuer),
]); ]);

View File

@ -125,6 +125,10 @@ export class URLRouter {
return window.location.origin; return window.location.origin;
} }
createOIDCRedirectURL() {
return window.location.origin;
}
normalizeUrl() { normalizeUrl() {
// Remove any queryParameters from the URL // Remove any queryParameters from the URL
// Gets rid of the loginToken after SSO // Gets rid of the loginToken after SSO

View File

@ -23,6 +23,7 @@ export class OIDCLoginMethod implements ILoginMethod {
private readonly _code: string; private readonly _code: string;
private readonly _codeVerifier: string; private readonly _codeVerifier: string;
private readonly _nonce: string; private readonly _nonce: string;
private readonly _redirectUri: string;
private readonly _oidcApi: OidcApi; private readonly _oidcApi: OidcApi;
public readonly homeserver: string; public readonly homeserver: string;
@ -31,18 +32,21 @@ export class OIDCLoginMethod implements ILoginMethod {
codeVerifier, codeVerifier,
code, code,
homeserver, homeserver,
redirectUri,
oidcApi, oidcApi,
}: { }: {
nonce: string, nonce: string,
code: string, code: string,
codeVerifier: string, codeVerifier: string,
homeserver: string, homeserver: string,
redirectUri: string,
oidcApi: OidcApi, oidcApi: OidcApi,
}) { }) {
this._oidcApi = oidcApi; this._oidcApi = oidcApi;
this._code = code; this._code = code;
this._codeVerifier = codeVerifier; this._codeVerifier = codeVerifier;
this._nonce = nonce; this._nonce = nonce;
this._redirectUri = redirectUri;
this.homeserver = homeserver; this.homeserver = homeserver;
} }
@ -50,6 +54,7 @@ export class OIDCLoginMethod implements ILoginMethod {
const { access_token, refresh_token, expires_in } = await this._oidcApi.completeAuthorizationCodeGrant({ const { access_token, refresh_token, expires_in } = await this._oidcApi.completeAuthorizationCodeGrant({
code: this._code, code: this._code,
codeVerifier: this._codeVerifier, codeVerifier: this._codeVerifier,
redirectUri: this._redirectUri,
}); });
// TODO: validate the id_token and the nonce claim // TODO: validate the id_token and the nonce claim

View File

@ -39,6 +39,7 @@ const isValidBearerToken = (t: any): t is BearerToken =>
type AuthorizationParams = { type AuthorizationParams = {
state: string, state: string,
scope: string, scope: string,
redirectUri: string,
nonce?: string, nonce?: string,
codeVerifier?: string, codeVerifier?: string,
}; };
@ -118,6 +119,7 @@ export class OidcApi {
async authorizationEndpoint({ async authorizationEndpoint({
state, state,
redirectUri,
scope, scope,
nonce, nonce,
codeVerifier, codeVerifier,
@ -126,7 +128,7 @@ export class OidcApi {
const url = new URL(metadata["authorization_endpoint"]); const url = new URL(metadata["authorization_endpoint"]);
url.searchParams.append("response_mode", "fragment"); url.searchParams.append("response_mode", "fragment");
url.searchParams.append("response_type", "code"); url.searchParams.append("response_type", "code");
url.searchParams.append("redirect_uri", this.redirectUri); url.searchParams.append("redirect_uri", redirectUri);
url.searchParams.append("client_id", this._clientId); url.searchParams.append("client_id", this._clientId);
url.searchParams.append("state", state); url.searchParams.append("state", state);
url.searchParams.append("scope", scope); url.searchParams.append("scope", scope);
@ -147,9 +149,10 @@ export class OidcApi {
return metadata["token_endpoint"]; return metadata["token_endpoint"];
} }
generateParams(scope: string): AuthorizationParams { generateParams({ scope, redirectUri }: { scope: string, redirectUri: string }): AuthorizationParams {
return { return {
scope, scope,
redirectUri,
state: randomString(8), state: randomString(8),
nonce: randomString(8), nonce: randomString(8),
codeVerifier: randomString(32), codeVerifier: randomString(32),
@ -159,12 +162,13 @@ export class OidcApi {
async completeAuthorizationCodeGrant({ async completeAuthorizationCodeGrant({
codeVerifier, codeVerifier,
code, code,
}: { codeVerifier: string, code: string }): Promise<BearerToken> { redirectUri,
}: { codeVerifier: string, code: string, redirectUri: string }): Promise<BearerToken> {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.append("grant_type", "authorization_code"); params.append("grant_type", "authorization_code");
params.append("client_id", this._clientId); params.append("client_id", this._clientId);
params.append("code_verifier", codeVerifier); params.append("code_verifier", codeVerifier);
params.append("redirect_uri", this.redirectUri); params.append("redirect_uri", redirectUri);
params.append("code", code); params.append("code", code);
const body = params.toString(); const body = params.toString();