From 08540778fbfae86d57b267c7dc8418c4fb0c8d16 Mon Sep 17 00:00:00 2001 From: Tim Keating Date: Thu, 1 Sep 2022 00:11:50 -0500 Subject: [PATCH 1/7] Modernized to current version of SvelteKit --- .gitignore | 3 ++- package.json | 4 +-- rollup.config.js | 2 +- src/hooks.ts | 43 ++++++++++++++++++++++++++++++++ src/integration.ts | 61 ++++++++++++---------------------------------- 5 files changed, 63 insertions(+), 50 deletions(-) create mode 100644 src/hooks.ts diff --git a/.gitignore b/.gitignore index 7ec6c61..cd62174 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /dist/ /docs/ /types/ -/pnpm-lock.yaml \ No newline at end of file +/pnpm-lock.yaml +.idea/ diff --git a/package.json b/package.json index ec918ec..e38cda3 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,9 @@ "build": "rollup -c", "lint": "eslint src/", "pretest:svelte": "rollup -c rollup.test.config.js", - "pretest:sveltekit": "npm run build", + "pretest:sveltekit": "pnpm run build", "test:svelte": "sirv tests/svelte", - "test:sveltekit": "cd tests/sk; npm run dev", + "test:sveltekit": "cd tests/sk; pnpm run dev", "prepublishOnly": "npm run build" }, "devDependencies": { diff --git a/rollup.config.js b/rollup.config.js index 2c1b46d..21c66be 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -10,7 +10,7 @@ export default { { file: pkg.module, 'format': 'es' }, { file: pkg.main, 'format': 'umd', name: 'Auth' } ], - external: ['$app/navigation', '$app/stores', '$app/env'], + external: ['$app/navigation', '$app/stores', '$app/environment'], plugins: [ svelte(), typescript(), diff --git a/src/hooks.ts b/src/hooks.ts new file mode 100644 index 0000000..46e9a65 --- /dev/null +++ b/src/hooks.ts @@ -0,0 +1,43 @@ +import type { Handle } from "@sveltejs/kit" + +import { getTokenStorageType } from "./oauth" +import { getResponseCookie, setRequestCookies } from "./oauth/tokenStorage/serverCookie" + +import { browser } from "$app/environment" + +/** + * Handle hooks for SSR + * @param {import("@sveltejs/kit/types/hooks").ServerRequest} request The server request + * @param {Function} resolve The request resolver + */ + +export const handle: Handle = async ({event, resolve}) => { + if (getTokenStorageType() === "cookie" && !browser) { + setRequestCookies(event.request.headers["cookie"] || "") + } + + const response = await resolve(event) + + return Promise.resolve(response).then((response: Response) => { + const cookies = getResponseCookie() + if (cookies !== "") { + let existing = response.headers["set-cookie"] || [] + if (typeof existing === "string") existing = [existing] + existing.push(cookies) + response.headers.set("set-cookie", existing) + } + + const redirection = this.getRedirection() + if (redirection !== null && redirection !== "null") { + response = { + ...response, + status: 302, + body: null + } + + response.headers.set("location", redirection) + } + + return response + }) +} diff --git a/src/integration.ts b/src/integration.ts index 39d6cd5..6b0640e 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -1,11 +1,10 @@ -import type {ServerResponse} from "@sveltejs/kit/types/hooks" -import { get } from "svelte/store" - import {getTokenStorageType} from "./oauth" import type {TokenStorage} from "./oauth/tokenStorage" import {browserCookie} from "./oauth/tokenStorage/browserCookie" import {localStorage} from "./oauth/tokenStorage/localStorage" -import {getResponseCookie, serverCookie, setRequestCookies} from "./oauth/tokenStorage/serverCookie" +import { serverCookie } from "./oauth/tokenStorage/serverCookie" + +import { browser } from "$app/environment" const inMemoryStorage: Record = {} @@ -58,9 +57,8 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt async redirect(url: string): Promise { const navigation = await import("$app/navigation") - const env = await import("$app/env") - if (env.browser) { + if (browser) { return navigation.goto(url) } else { this.redirectedTo = url @@ -72,8 +70,14 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt if (this.queryObject !== null) { return Promise.resolve(this.queryObject) } - const stores = await import("$app/stores") - return get(stores.page).query + + // Old version + // const stores = await import("$app/stores") + // return get(stores.page).query + + // New version, except Page doesn't HAVE a query + // const page = getStores().page + // return page.query } getRedirection(): string|null { @@ -99,56 +103,21 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt } async tokenStorage(): Promise { - const env = await import("$app/env") if (getTokenStorageType() === "cookie") { - return env.browser ? browserCookie : serverCookie + return browser ? browserCookie : serverCookie } return localStorage } - /** - * Handle hooks for SSR - * @param {import("@sveltejs/kit/types/hooks").ServerRequest} request The server request - * @param {Function} resolve The request resolver - */ - async handleHook({request, resolve}) { - const env = await import("$app/env") - - if (getTokenStorageType() === "cookie" && !env.browser) { - setRequestCookies(request.headers["cookie"] || "") - } - /** @type {Promise} response */ - const response = resolve(request) - - return Promise.resolve(response).then((response: ServerResponse) => { - const cookies = getResponseCookie() - if (cookies !== "") { - let existing = response.headers["set-cookie"] || [] - if (typeof existing === "string") existing = [existing] - existing.push(cookies) - response.headers["set-cookie"] = existing - } - const redirection = this.getRedirection() - if (redirection !== null && redirection !== "null") { - response.status = 302 - response.headers.location = redirection - response.body = null - } - return response - }) - } - async getFromTemporary(key: string): Promise { - const env = await import("$app/env") - if (!env.browser) { + if (!browser) { return inMemoryStorage[key] || null } return window.sessionStorage.getItem(key) } async saveInTemporary(key: string, data: string) { - const env = await import("$app/env") - if (!env.browser) { + if (!browser) { inMemoryStorage[key] = data return } From 0c0fba9af17bb8e3b8d585db7ff57f91528d38ff Mon Sep 17 00:00:00 2001 From: Tim Keating Date: Sat, 3 Sep 2022 19:19:40 -0500 Subject: [PATCH 2/7] Modernization WIP --- package.json | 5 +++-- rollup.config.js | 23 ++++++++++++----------- src/hooks.ts | 3 +++ src/oauth/grant/pkce.ts | 2 +- svelte.config.js | 5 ----- 5 files changed, 19 insertions(+), 19 deletions(-) delete mode 100644 svelte.config.js diff --git a/package.json b/package.json index e38cda3..100119e 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "OAuth2 authorization for Svelte", "author": "MacFJA", "license": "MIT", - "version": "1.1.0", + "version": "1.1.1", "bugs": { "url": "https://github.com/macfja/svelte-oauth2/issues" }, @@ -73,6 +73,7 @@ "cookie": "^0.4.1", "js-base64": "^3.6.1", "js-cookie": "^3.0.1", - "pkce": "^1.0.0-beta2" + "pkce": "^1.0.0-beta2", + "vite": "^3.1.0-beta.1" } } diff --git a/rollup.config.js b/rollup.config.js index 21c66be..9016094 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,20 +1,21 @@ -import svelte from 'rollup-plugin-svelte'; -import resolve from '@rollup/plugin-node-resolve'; -import typescript from "@rollup/plugin-typescript"; -import commonjs from "@rollup/plugin-commonjs"; -import pkg from './package.json'; +import commonjs from "@rollup/plugin-commonjs" +import resolve from "@rollup/plugin-node-resolve" +import typescript from "@rollup/plugin-typescript" +import svelte from "rollup-plugin-svelte" + +import pkg from "./package.json" export default { - input: 'src/index.ts', + input: "src/index.ts", output: [ - { file: pkg.module, 'format': 'es' }, - { file: pkg.main, 'format': 'umd', name: 'Auth' } + { file: pkg.module, "format": "es" }, + { file: pkg.main, "format": "umd", name: "Auth" } ], - external: ['$app/navigation', '$app/stores', '$app/environment'], + external: ["$app/navigation", "$app/stores", "$app/environment"], plugins: [ svelte(), typescript(), - commonjs({ignore: ['crypto']}), + commonjs({ignore: ["crypto"]}), resolve() ] -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/hooks.ts b/src/hooks.ts index 46e9a65..081ee97 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -27,7 +27,10 @@ export const handle: Handle = async ({event, resolve}) => { response.headers.set("set-cookie", existing) } + // eslint-disable @typescript-eslint/ban-ts-comment + // @ts-ignore: Object is possibly 'undefined'. const redirection = this.getRedirection() + if (redirection !== null && redirection !== "null") { response = { ...response, diff --git a/src/oauth/grant/pkce.ts b/src/oauth/grant/pkce.ts index ba0bea2..9494299 100644 --- a/src/oauth/grant/pkce.ts +++ b/src/oauth/grant/pkce.ts @@ -38,7 +38,7 @@ export class AuthorizationCodePKCE extends BaseGrant implements Grant async onRequest(): Promise { const params = await this.integration.query() - if (params.has("code") && params.has("state")) { + if (params?.has("code") && params?.has("state")) { const state = params.get("state") const code = params.get("code") diff --git a/svelte.config.js b/svelte.config.js deleted file mode 100644 index b14355e..0000000 --- a/svelte.config.js +++ /dev/null @@ -1,5 +0,0 @@ -const sveltePreprocess = require("svelte-preprocess"); - -module.exports = { - preprocess: sveltePreprocess(), -}; \ No newline at end of file From 4dff66c77211960b05d71638b146e419d17dbd72 Mon Sep 17 00:00:00 2001 From: Tim Keating Date: Fri, 16 Sep 2022 22:27:47 -0500 Subject: [PATCH 3/7] Put hook handler back into sveltekitStrategy --- src/hooks.ts | 46 -------------------------- src/integration.ts | 71 ++++++++++++++++++++++++++++++++++------- src/oauth/grant.ts | 10 +++--- src/oauth/grant/pkce.ts | 2 ++ 4 files changed, 68 insertions(+), 61 deletions(-) delete mode 100644 src/hooks.ts diff --git a/src/hooks.ts b/src/hooks.ts deleted file mode 100644 index 081ee97..0000000 --- a/src/hooks.ts +++ /dev/null @@ -1,46 +0,0 @@ -import type { Handle } from "@sveltejs/kit" - -import { getTokenStorageType } from "./oauth" -import { getResponseCookie, setRequestCookies } from "./oauth/tokenStorage/serverCookie" - -import { browser } from "$app/environment" - -/** - * Handle hooks for SSR - * @param {import("@sveltejs/kit/types/hooks").ServerRequest} request The server request - * @param {Function} resolve The request resolver - */ - -export const handle: Handle = async ({event, resolve}) => { - if (getTokenStorageType() === "cookie" && !browser) { - setRequestCookies(event.request.headers["cookie"] || "") - } - - const response = await resolve(event) - - return Promise.resolve(response).then((response: Response) => { - const cookies = getResponseCookie() - if (cookies !== "") { - let existing = response.headers["set-cookie"] || [] - if (typeof existing === "string") existing = [existing] - existing.push(cookies) - response.headers.set("set-cookie", existing) - } - - // eslint-disable @typescript-eslint/ban-ts-comment - // @ts-ignore: Object is possibly 'undefined'. - const redirection = this.getRedirection() - - if (redirection !== null && redirection !== "null") { - response = { - ...response, - status: 302, - body: null - } - - response.headers.set("location", redirection) - } - - return response - }) -} diff --git a/src/integration.ts b/src/integration.ts index 6b0640e..daddb8e 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -1,8 +1,10 @@ -import {getTokenStorageType} from "./oauth" -import type {TokenStorage} from "./oauth/tokenStorage" -import {browserCookie} from "./oauth/tokenStorage/browserCookie" -import {localStorage} from "./oauth/tokenStorage/localStorage" -import { serverCookie } from "./oauth/tokenStorage/serverCookie" +import { debug } from "svelte/internal" + +import { getTokenStorageType } from "./oauth" +import type { TokenStorage } from "./oauth/tokenStorage" +import { browserCookie } from "./oauth/tokenStorage/browserCookie" +import { localStorage } from "./oauth/tokenStorage/localStorage" +import { getResponseCookie, serverCookie, setRequestCookies } from "./oauth/tokenStorage/serverCookie" import { browser } from "$app/environment" @@ -25,7 +27,7 @@ export interface ContextStrategy { * @param {string} uri The URI of the data * @param {Record} [options] Fetch options */ - fetch(uri:string, options?:Record): Promise, + fetch(uri: string, options?: Record): Promise, /** * Get the storage where token is saved @@ -36,7 +38,7 @@ export interface ContextStrategy { * Get data from the temporary storage * @param {string} key The name/key of the data */ - getFromTemporary(key: string): Promise, + getFromTemporary(key: string): Promise, /** * Save data in the temporary storage @@ -49,7 +51,7 @@ export interface ContextStrategy { export const svelteKitStrategy: ContextStrategy = new class implements ContextStrategy { private fetchFunc private redirectedTo = null - private queryObject: URLSearchParams|null = null + private queryObject: URLSearchParams | null = null fetch(uri: string, options?: Record): Promise { return this.fetchFunc(uri, options) @@ -75,12 +77,12 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt // const stores = await import("$app/stores") // return get(stores.page).query - // New version, except Page doesn't HAVE a query + // New version, except the page store is a Readable and can only be subscribed // const page = getStores().page - // return page.query + // return page.url.searchParams } - getRedirection(): string|null { + getRedirection(): string | null { const redirection = this.redirectedTo + "" this.redirectedTo = null return redirection @@ -118,12 +120,56 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt async saveInTemporary(key: string, data: string) { if (!browser) { + debug("integration.ts", "", "", "Saving in in-memory storage") inMemoryStorage[key] = data return } + debug("integration.ts", "", "", "Saving in browser session storage") return window.sessionStorage.setItem(key, data) } + /** + * Handle hooks for SSR + * @param {import("@sveltejs/kit/types/hooks").ServerRequest} request The server request + * @param {Function} resolve The request resolver + */ + async handleHook({event, resolve}) { + debug("integration.ts", "", "", "Handle hook") + if (getTokenStorageType() === "cookie" && !browser) { + setRequestCookies(event.request.headers["cookie"] || "") + } + + const response = await resolve(event) + + return Promise.resolve(response).then((response: Response) => { + const cookies = getResponseCookie() + debug("integration.ts", "", "", cookies) + if (cookies !== "") { + let existing = response.headers["set-cookie"] || [] + if (typeof existing === "string") existing = [existing] + existing.push(cookies) + response.headers.set("set-cookie", existing) + } + + // eslint-disable @typescript-eslint/ban-ts-comment + // @ts-ignore: Object is possibly 'undefined'. + const redirection = this.getRedirection() + + if (redirection !== null && redirection !== "null") { + response = { + ...response, + status: 302, + body: null + } + + response.headers.set("location", redirection) + } + + return response + }) + } + + } export const browserStrategy: ContextStrategy = new class implements ContextStrategy { @@ -131,12 +177,15 @@ export const browserStrategy: ContextStrategy = new class implements ContextStra window.location.href = url return Promise.resolve() } + query(): Promise { return Promise.resolve(new URL(window.location.href).searchParams) } + fetch(uri: string, options?: Record): Promise { return fetch(uri, options) } + tokenStorage(): Promise { if (getTokenStorageType() === "cookie") { return Promise.resolve(browserCookie) diff --git a/src/oauth/grant.ts b/src/oauth/grant.ts index 426f1f3..94bf26f 100644 --- a/src/oauth/grant.ts +++ b/src/oauth/grant.ts @@ -32,15 +32,17 @@ export abstract class BaseGrant implements Grant { } protected getToken(params: Record, headers: HeadersInit = {}): Promise { - const requestHeader = new Headers(headers) - requestHeader.set("content-type", "application/json") + const requestHeaders = new Headers() + requestHeaders.set("content-type", "application/json") return this.integration.fetch(this.tokenUri, { method: "post", body: JSON.stringify(params), - headers: requestHeader + headers: requestHeaders }) - .then((response) => response.json()) + .then((response) => { + return response.json() + }) .then(async (response) => { if (Object.keys(response).includes("error")) { (await this.integration.tokenStorage()).set(null) diff --git a/src/oauth/grant/pkce.ts b/src/oauth/grant/pkce.ts index 9494299..1c816ed 100644 --- a/src/oauth/grant/pkce.ts +++ b/src/oauth/grant/pkce.ts @@ -5,6 +5,7 @@ import type {ContextStrategy} from "../../integration" import {ManInTheMiddle} from "../exception/ManInTheMiddle" import {BaseGrant} from "../grant" import type {Grant} from "../grant" +import { debug } from "svelte/internal"; export class AuthorizationCodePKCE extends BaseGrant implements Grant { @@ -38,6 +39,7 @@ export class AuthorizationCodePKCE extends BaseGrant implements Grant async onRequest(): Promise { const params = await this.integration.query() + debug("AuthorizationCodePKCE", "", "", params) if (params?.has("code") && params?.has("state")) { const state = params.get("state") const code = params.get("code") From a811bd8dadaa76a9017c0dddb774f222e18b712f Mon Sep 17 00:00:00 2001 From: Tim Keating Date: Fri, 16 Sep 2022 22:33:55 -0500 Subject: [PATCH 4/7] Move get/set temporary to clean up diffs --- src/integration.ts | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/integration.ts b/src/integration.ts index daddb8e..221493a 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -111,23 +111,6 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt return localStorage } - async getFromTemporary(key: string): Promise { - if (!browser) { - return inMemoryStorage[key] || null - } - return window.sessionStorage.getItem(key) - } - - async saveInTemporary(key: string, data: string) { - if (!browser) { - debug("integration.ts", "", "", "Saving in in-memory storage") - inMemoryStorage[key] = data - return - } - debug("integration.ts", "", "", "Saving in browser session storage") - return window.sessionStorage.setItem(key, data) - } - /** * Handle hooks for SSR * @param {import("@sveltejs/kit/types/hooks").ServerRequest} request The server request @@ -169,7 +152,22 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt }) } + async getFromTemporary(key: string): Promise { + if (!browser) { + return inMemoryStorage[key] || null + } + return window.sessionStorage.getItem(key) + } + async saveInTemporary(key: string, data: string) { + if (!browser) { + debug("integration.ts", "", "", "Saving in in-memory storage") + inMemoryStorage[key] = data + return + } + debug("integration.ts", "", "", "Saving in browser session storage") + return window.sessionStorage.setItem(key, data) + } } export const browserStrategy: ContextStrategy = new class implements ContextStrategy { From 58a465b6cd3b048d7718ab68e4ad1497d21d4a4b Mon Sep 17 00:00:00 2001 From: Tim Keating Date: Fri, 16 Sep 2022 22:36:05 -0500 Subject: [PATCH 5/7] Not sure why I added an await here... --- src/integration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/integration.ts b/src/integration.ts index 221493a..70dc560 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -122,7 +122,7 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt setRequestCookies(event.request.headers["cookie"] || "") } - const response = await resolve(event) + const response = resolve(event) return Promise.resolve(response).then((response: Response) => { const cookies = getResponseCookie() From 57ad44c927f398eda1c3c380fb429de938d07a77 Mon Sep 17 00:00:00 2001 From: Tim Keating Date: Fri, 23 Sep 2022 19:39:35 -0500 Subject: [PATCH 6/7] Remove storage of the fetch function (svelte now provides this on the server side) --- src/integration.ts | 80 +++++++++++++++------------------------------- 1 file changed, 25 insertions(+), 55 deletions(-) diff --git a/src/integration.ts b/src/integration.ts index 70dc560..7540a6a 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -20,14 +20,7 @@ export interface ContextStrategy { * Redirect to an url * @param {string} url */ - redirect(url: string): Promise - - /** - * Get data from an URL (Fetch API) - * @param {string} uri The URI of the data - * @param {Record} [options] Fetch options - */ - fetch(uri: string, options?: Record): Promise, + redirect(url: string): Promise, /** * Get the storage where token is saved @@ -53,10 +46,6 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt private redirectedTo = null private queryObject: URLSearchParams | null = null - fetch(uri: string, options?: Record): Promise { - return this.fetchFunc(uri, options) - } - async redirect(url: string): Promise { const navigation = await import("$app/navigation") @@ -88,14 +77,6 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt return redirection } - /** - * Set the fetch function to use - * @param {Function} func - */ - setFetch(func) { - this.fetchFunc = func - } - /** * Set the request Query * @param query @@ -113,43 +94,38 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt /** * Handle hooks for SSR - * @param {import("@sveltejs/kit/types/hooks").ServerRequest} request The server request - * @param {Function} resolve The request resolver + * https://kit.svelte.dev/docs/types#sveltejs-kit-handle */ async handleHook({event, resolve}) { - debug("integration.ts", "", "", "Handle hook") if (getTokenStorageType() === "cookie" && !browser) { setRequestCookies(event.request.headers["cookie"] || "") } - const response = resolve(event) - - return Promise.resolve(response).then((response: Response) => { - const cookies = getResponseCookie() - debug("integration.ts", "", "", cookies) - if (cookies !== "") { - let existing = response.headers["set-cookie"] || [] - if (typeof existing === "string") existing = [existing] - existing.push(cookies) - response.headers.set("set-cookie", existing) - } - - // eslint-disable @typescript-eslint/ban-ts-comment - // @ts-ignore: Object is possibly 'undefined'. - const redirection = this.getRedirection() - - if (redirection !== null && redirection !== "null") { - response = { - ...response, - status: 302, - body: null - } + const response = await resolve(event) + + const cookies = getResponseCookie() + if (cookies !== "") { + let existing = response.headers["set-cookie"] || [] + if (typeof existing === "string") existing = [existing] + existing.push(cookies) + response.headers.set("set-cookie", existing) + } + + // eslint-disable @typescript-eslint/ban-ts-comment + // @ts-ignore: Object is possibly 'undefined'. + const redirection = this.getRedirection() - response.headers.set("location", redirection) - } + if (redirection !== null && redirection !== "null") { + return new Response(null, { + status: 302, + headers: { + ...response.headers, + location: redirection + } + }) + } - return response - }) + return response } async getFromTemporary(key: string): Promise { @@ -161,11 +137,9 @@ export const svelteKitStrategy: ContextStrategy = new class implements ContextSt async saveInTemporary(key: string, data: string) { if (!browser) { - debug("integration.ts", "", "", "Saving in in-memory storage") inMemoryStorage[key] = data return } - debug("integration.ts", "", "", "Saving in browser session storage") return window.sessionStorage.setItem(key, data) } } @@ -180,10 +154,6 @@ export const browserStrategy: ContextStrategy = new class implements ContextStra return Promise.resolve(new URL(window.location.href).searchParams) } - fetch(uri: string, options?: Record): Promise { - return fetch(uri, options) - } - tokenStorage(): Promise { if (getTokenStorageType() === "cookie") { return Promise.resolve(browserCookie) From 9816c3202804c769a5e187c4fe05e38b46396812 Mon Sep 17 00:00:00 2001 From: Tim Keating Date: Fri, 23 Sep 2022 19:40:58 -0500 Subject: [PATCH 7/7] Update grant interface to allow setting additional headers --- src/oauth/grant.ts | 12 +++++++++--- src/oauth/grant/pkce.ts | 17 ++++++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/oauth/grant.ts b/src/oauth/grant.ts index 94bf26f..48623ff 100644 --- a/src/oauth/grant.ts +++ b/src/oauth/grant.ts @@ -25,17 +25,19 @@ export interface Grant { export abstract class BaseGrant implements Grant { protected integration: ContextStrategy private tokenUri: string + protected headers: Headers - constructor(integration: ContextStrategy, tokenUri: string) { + constructor(integration: ContextStrategy, tokenUri: string, headers?: Headers) { this.integration = integration this.tokenUri = tokenUri + this.headers = headers || new Headers() } protected getToken(params: Record, headers: HeadersInit = {}): Promise { - const requestHeaders = new Headers() + const requestHeaders = new Headers(headers) requestHeaders.set("content-type", "application/json") - return this.integration.fetch(this.tokenUri, { + return fetch(this.tokenUri, { method: "post", body: JSON.stringify(params), headers: requestHeaders @@ -52,6 +54,10 @@ export abstract class BaseGrant implements Grant { } return response }) + .catch(({reason, }) => { + console.log(`getToken failed: ${reason}`) + return Promise.resolve(false) + }) } onRequest(): Promise { diff --git a/src/oauth/grant/pkce.ts b/src/oauth/grant/pkce.ts index 1c816ed..fcbf1d7 100644 --- a/src/oauth/grant/pkce.ts +++ b/src/oauth/grant/pkce.ts @@ -21,6 +21,7 @@ export class AuthorizationCodePKCE extends BaseGrant implements Grant * @param {string} tokenUri The Auth Server URI where to get the access token. * @param {string} authorizationUri The Auth Server URI where to go for authentication. * @param {string} authorizationRedirectUri The application URI to go back from the Auth Server + * @param headers optional {Headers} Additional headers that will be passed as part of the bearer token request (e.g. 'X-API-Key') */ constructor( integration: ContextStrategy, @@ -28,9 +29,10 @@ export class AuthorizationCodePKCE extends BaseGrant implements Grant postLoginRedirectUri: string, tokenUri: string, authorizationUri: string, - authorizationRedirectUri: string + authorizationRedirectUri: string, + headers?: Headers ) { - super(integration, tokenUri) + super(integration, tokenUri, headers) this.authorizationRedirectUri = authorizationRedirectUri this.authorizationUri = authorizationUri this.clientId = clientId @@ -39,7 +41,6 @@ export class AuthorizationCodePKCE extends BaseGrant implements Grant async onRequest(): Promise { const params = await this.integration.query() - debug("AuthorizationCodePKCE", "", "", params) if (params?.has("code") && params?.has("state")) { const state = params.get("state") const code = params.get("code") @@ -50,18 +51,24 @@ export class AuthorizationCodePKCE extends BaseGrant implements Grant return this.getToken({ grant_type: "authorization_code", code: code, - client_id: this.clientId, + client_id: this.clientId, redirect_uri: this.postLoginRedirectUri, code_verifier: await this.integration.getFromTemporary("svelte-oauth-code-verifier") - }).then(async () => { + }, + this.headers + ).then(async () => { await this.integration.redirect(this.postLoginRedirectUri) return Promise.resolve(true) + }).catch(reason => { + console.log(reason) + return Promise.resolve(false) }) } return super.onRequest() } async onUnauthenticated(scopes: Array): Promise { await super.onUnauthenticated(scopes) + await this.integration.saveInTemporary("svelte-oauth-tries", "0") const url = new URL(this.authorizationUri) url.searchParams.append("response_type", "code") url.searchParams.append("scope", scopes.join(" "))