Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,24 @@
}
</script>
<script type="module">
import { CachingIssuerProvider, DPoPTokenProvider, ReactiveFetchManager } from "./dist/mod.js"
import {
CachingAuthorizationServerProvider,
CachingIssuerProvider,
DPoPTokenProvider,
ReactiveFetchManager,
XASProvider
} from "./dist/mod.js"
import "./dist/registerElements.js"

/* Reactive fetch infrastructure */
const ui = document.querySelector("authorization-code-flow")
const issuerUi = document.querySelector("idp-picker")
const issuerProvider = new CachingIssuerProvider(issuerUi)
const asProvider = new XASProvider(issuerProvider)
const cachingASProvider = new CachingAuthorizationServerProvider(asProvider)
const callbackUri = new URL("/callback.html", location.href).toString()

const dPoPTokenProvider = new DPoPTokenProvider(callbackUri, ui.getCode.bind(ui), issuerProvider)
const dPoPTokenProvider = new DPoPTokenProvider(callbackUri, ui.getCode.bind(ui), cachingASProvider)

const fetch = new ReactiveFetchManager([dPoPTokenProvider]).fetch

Expand Down
5 changes: 5 additions & 0 deletions src/AuthorizationServerProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as oauth from "oauth4webapi"

export interface AuthorizationServerProvider {
getAuthorizationServer(request: Request): Promise<oauth.AuthorizationServer>
}
15 changes: 15 additions & 0 deletions src/CachingAuthorizationServerProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { AuthorizationServerProvider } from "./AuthorizationServerProvider.js"
import type * as oauth from "oauth4webapi"

export class CachingAuthorizationServerProvider implements AuthorizationServerProvider {
readonly #cache = new Map<string, Promise<oauth.AuthorizationServer>> // TODO: Take cache from caller
readonly #original: AuthorizationServerProvider

constructor(original: AuthorizationServerProvider) {
this.#original = original
}

getAuthorizationServer(request: Request): Promise<oauth.AuthorizationServer> {
return this.#cache.getOrInsertComputed(request.url, _ => this.#original.getAuthorizationServer(request))
}
}
13 changes: 5 additions & 8 deletions src/DPoPTokenProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@ import * as oauth from "oauth4webapi"
import * as DPoP from "dpop"
import type { GetCodeCallback } from "./GetCodeCallback.js"
import type { TokenProvider } from "./TokenProvider.js"
import type { IssuerProvider } from "./IssuerProvider.js"
import type { AuthorizationServerProvider } from "./AuthorizationServerProvider.js"

type CacheEntry = { created: number, tokenResult: oauth.TokenEndpointResponse, dpopKey: CryptoKeyPair }

export class DPoPTokenProvider implements TokenProvider {
readonly #getCode: GetCodeCallback
readonly #callbackUri: string
readonly #cache = new Map<string, CacheEntry> // TODO: Take cache from caller
readonly #issuerProvider: IssuerProvider
readonly #asProvider: AuthorizationServerProvider

constructor(callbackUri: string, getCodeCallback: GetCodeCallback, issuerProvider: IssuerProvider) {
constructor(callbackUri: string, getCodeCallback: GetCodeCallback, asProvider: AuthorizationServerProvider) {
this.#getCode = getCodeCallback
this.#callbackUri = callbackUri
this.#issuerProvider = issuerProvider
this.#asProvider = asProvider
}

async matches(request: Request): Promise<boolean> {
Expand All @@ -38,10 +38,7 @@ export class DPoPTokenProvider implements TokenProvider {
return new Request(request, {headers})
}
private async obtainToken(request: Request): Promise<CacheEntry> {
const issuer = await this.#issuerProvider.getIssuer(request)

const discoveryResponse = await oauth.discoveryRequest(issuer, {signal: request.signal})
const authorizationServer = await oauth.processDiscoveryResponse(issuer, discoveryResponse)
const authorizationServer = await this.#asProvider.getAuthorizationServer(request)

const registrationResponse = await oauth.dynamicClientRegistrationRequest(authorizationServer, {redirect_uris: [this.#callbackUri]}, {signal: request.signal})
const clientRegistration = await oauth.processDynamicClientRegistrationResponse(registrationResponse)
Expand Down
19 changes: 19 additions & 0 deletions src/XASProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { IssuerProvider } from "./IssuerProvider.js"
import * as oauth from "oauth4webapi"
import type { AuthorizationServerProvider } from "./AuthorizationServerProvider.js"

export class XASProvider implements AuthorizationServerProvider {
readonly #issuerProvider: IssuerProvider

constructor(issuerProvider: IssuerProvider) {
this.#issuerProvider = issuerProvider
}

async getAuthorizationServer(request: Request): Promise<oauth.AuthorizationServer> {
const issuer = await this.#issuerProvider.getIssuer(request)
const discoveryResponse = await oauth.discoveryRequest(issuer, {signal: request.signal})
const authorizationServer = await oauth.processDiscoveryResponse(issuer, discoveryResponse)

return authorizationServer
}
}
3 changes: 3 additions & 0 deletions src/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ export * from "./WebIdPicker.js"
export * from "./WebIdRequestCancelledError.js"
export * from "./IssuerRequestCancelledError.js"
export * from "./CachingIssuerProvider.js"
export * from "./AuthorizationServerProvider.js"
export * from "./XASProvider.js"
export * from "./CachingAuthorizationServerProvider.js"