-
Notifications
You must be signed in to change notification settings - Fork 2
Improve window handling in Authorization Code flow #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { Mutex } from "./Mutex.js" | ||
| import { CodeRequestCancelledError } from "./CodeRequestCancelledError.js" | ||
| import type { CodeProvider } from "./CodeProvider.js" | ||
|
|
||
| const authorizationWindowName = "oidcAuthentication" | ||
| const onlyOnce = {once: true} | ||
|
|
@@ -119,7 +120,7 @@ const html = ` | |
| * </style> | ||
| * ``` | ||
| */ | ||
| export class AuthorizationCodeFlow extends HTMLElement { | ||
| export class AuthorizationCodeFlow extends HTMLElement implements CodeProvider { | ||
| readonly #mutex = new Mutex | ||
| #newModal!: HTMLDialogElement | ||
| #switchModal!: HTMLDialogElement | ||
|
|
@@ -194,7 +195,6 @@ export class AuthorizationCodeFlow extends HTMLElement { | |
| this.ownerDocument.defaultView?.removeEventListener("message", onMessage) | ||
| signal.removeEventListener("abort", onAbort) | ||
| this.#switchModal.close() | ||
| this.#authorizationWindow?.close() | ||
| respondWithCode(message.data) | ||
| } | ||
|
|
||
|
|
@@ -218,6 +218,10 @@ export class AuthorizationCodeFlow extends HTMLElement { | |
| return await responseFromPopup | ||
| } | ||
|
|
||
| cleanup(): void { | ||
| this.#authorizationWindow?.close() | ||
| } | ||
|
Comment on lines
+221
to
+223
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider making the This also enables the new This also applies to any other instances of
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| #onSubmit(e: SubmitEvent) { | ||
| e.preventDefault() | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| export interface CodeProvider { | ||
| getCode(authorizationUri: URL, signal: AbortSignal): Promise<string> | ||
|
|
||
| cleanup(): void | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,21 +1,21 @@ | ||
| import * as oauth from "oauth4webapi" | ||
| import * as DPoP from "dpop" | ||
| import type { GetCodeCallback } from "./GetCodeCallback.js" | ||
| import type { CodeProvider } from "./CodeProvider.js" | ||
| import type { TokenProvider } from "./TokenProvider.js" | ||
| import type { AuthorizationServerProvider } from "./AuthorizationServerProvider.js" | ||
| import { ClientProvider } from "./ClientProvider.js" | ||
|
|
||
| type CacheEntry = { created: number, tokenResult: oauth.TokenEndpointResponse, dpopKey: CryptoKeyPair } | ||
|
|
||
| export class DPoPTokenProvider implements TokenProvider { | ||
| readonly #getCode: GetCodeCallback | ||
| readonly #codeProvider: CodeProvider | ||
| readonly #callbackUri: string | ||
| readonly #cache = new Map<string, CacheEntry> // TODO: Take cache from caller | ||
| readonly #asProvider: AuthorizationServerProvider | ||
| readonly #clientProvider: ClientProvider | ||
|
|
||
| constructor(callbackUri: string, getCodeCallback: GetCodeCallback, asProvider: AuthorizationServerProvider, clientProvider: ClientProvider) { | ||
| this.#getCode = getCodeCallback | ||
| constructor(callbackUri: string, codeProvider: CodeProvider, asProvider: AuthorizationServerProvider, clientProvider: ClientProvider) { | ||
| this.#codeProvider = codeProvider | ||
| this.#callbackUri = callbackUri | ||
| this.#asProvider = asProvider | ||
| this.#clientProvider = clientProvider | ||
|
|
@@ -73,7 +73,7 @@ export class DPoPTokenProvider implements TokenProvider { | |
| } | ||
| } | ||
|
|
||
| const authorizationCodeResponse = await this.#getCode(authorizationUrl, request.signal) | ||
| const authorizationCodeResponse = await this.#codeProvider.getCode(authorizationUrl, request.signal) | ||
|
|
||
| let authorizationCodeParams | ||
| try { | ||
|
|
@@ -89,13 +89,14 @@ export class DPoPTokenProvider implements TokenProvider { | |
| console.debug("Authorization server requires user interaction, retrying without prompt") | ||
|
|
||
| authorizationUrl.searchParams.delete("prompt") | ||
| const authorizationCodeResponse = await this.#getCode(authorizationUrl, request.signal) | ||
| const authorizationCodeResponse = await this.#codeProvider.getCode(authorizationUrl, request.signal) | ||
| authorizationCodeParams = oauth.validateAuthResponse(authorizationServer, clientRegistration, new URL(authorizationCodeResponse), state) | ||
| } else { | ||
| throw e | ||
| } | ||
| } | ||
|
|
||
| this.#codeProvider.cleanup() | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And this is the counterpart: Closing the authorization window from the caller, crucially after a potential fallback in the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Following https://github.com/solid-contrib/reactive-authentication/pull/37/changes#r3853649238 - this would be a good place to make use of |
||
| const tokenResponse = await oauth.authorizationCodeGrantRequest(authorizationServer, clientRegistration, this.getClientAuth(authorizationServer.issuer, clientRegistration), authorizationCodeParams, this.#callbackUri, authorizationServer.code_challenge_methods_supported !== undefined ? codeVerifier : oauth.nopkce, {DPoP: dpop, signal: request.signal}) | ||
|
|
||
| const tokenResult = await oauth.processAuthorizationCodeResponse(authorizationServer, clientRegistration, tokenResponse, {expectedNonce: this.nonceVerificationOverride(authorizationServer.issuer, nonce)}) | ||
|
|
||
This file was deleted.

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First substantial change is not closing the authorization window in the provider.