Skip to content
This repository has been archived by the owner on Jan 10, 2024. It is now read-only.

Commit

Permalink
Merge pull request #1 from kyxyes/update
Browse files Browse the repository at this point in the history
feat: add DPoP feature
  • Loading branch information
keatsk authored Feb 27, 2023
2 parents 23bf295 + bf00d1e commit 2f9c7ab
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 2 deletions.
13 changes: 12 additions & 1 deletion docs/oidc-client-ts.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ export type CreateSignoutRequestArgs = Omit<SignoutRequestArgs, "url" | "state_d
state?: unknown;
};

// @public (undocumented)
export const DpopKeypair: {
publicKey: null;
privateKey: null;
};

// @public
export class ErrorResponse extends Error {
constructor(args: {
Expand Down Expand Up @@ -341,6 +347,7 @@ export interface OidcClientSettings {
clockSkewInSeconds?: number;
disablePKCE?: boolean;
display?: string;
enable_dpop?: boolean;
extraQueryParams?: Record<string, string | number | boolean>;
// (undocumented)
extraTokenParams?: Record<string, unknown>;
Expand Down Expand Up @@ -374,7 +381,7 @@ export interface OidcClientSettings {

// @public
export class OidcClientSettingsStore {
constructor({ authority, metadataUrl, metadata, signingKeys, metadataSeed, client_id, client_secret, response_type, scope, redirect_uri, post_logout_redirect_uri, client_authentication, prompt, display, max_age, ui_locales, acr_values, resource, response_mode, filterProtocolClaims, loadUserInfo, staleStateAgeInSeconds, clockSkewInSeconds, userInfoJwtIssuer, mergeClaims, disablePKCE, stateStore, refreshTokenCredentials, revokeTokenAdditionalContentTypes, fetchRequestCredentials, refreshTokenAllowedScope, extraQueryParams, extraTokenParams, }: OidcClientSettings);
constructor({ authority, metadataUrl, metadata, signingKeys, metadataSeed, client_id, client_secret, response_type, scope, redirect_uri, post_logout_redirect_uri, client_authentication, prompt, display, max_age, ui_locales, acr_values, resource, response_mode, filterProtocolClaims, loadUserInfo, staleStateAgeInSeconds, clockSkewInSeconds, userInfoJwtIssuer, mergeClaims, disablePKCE, stateStore, refreshTokenCredentials, revokeTokenAdditionalContentTypes, fetchRequestCredentials, refreshTokenAllowedScope, extraQueryParams, extraTokenParams, enable_dpop, }: OidcClientSettings);
// (undocumented)
readonly acr_values: string | undefined;
// (undocumented)
Expand All @@ -392,6 +399,8 @@ export class OidcClientSettingsStore {
// (undocumented)
readonly display: string | undefined;
// (undocumented)
readonly enable_dpop?: boolean;
// (undocumented)
readonly extraQueryParams: Record<string, string | number | boolean>;
// (undocumented)
readonly extraTokenParams: Record<string, unknown>;
Expand Down Expand Up @@ -1018,6 +1027,8 @@ export interface UserManagerSettings extends OidcClientSettings {
accessTokenExpiringNotificationTimeInSeconds?: number;
automaticSilentRenew?: boolean;
checkSessionIntervalInSeconds?: number;
// (undocumented)
enable_dpop?: boolean;
iframeNotifyParentOrigin?: string;
iframeScriptOrigin?: string;
includeIdTokenInSilentRenew?: boolean;
Expand Down
16 changes: 16 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
},
"dependencies": {
"crypto-js": "^4.1.1",
"dpop": "^1.1.0",
"jwt-decode": "^3.1.2"
},
"devDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions src/JsonService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface PostFormOpts {
basicAuth?: string;
timeoutInSeconds?: number;
initCredentials?: "same-origin" | "include" | "omit";
dpopHeader?: string;
}

/**
Expand Down Expand Up @@ -127,12 +128,16 @@ export class JsonService {
basicAuth,
timeoutInSeconds,
initCredentials,
dpopHeader,
}: PostFormOpts): Promise<Record<string, unknown>> {
const logger = this._logger.create("postForm");
const headers: HeadersInit = {
"Accept": this._contentTypes.join(", "),
"Content-Type": "application/x-www-form-urlencoded",
};
if (dpopHeader) {
headers["DPoP"] = dpopHeader;
}
if (basicAuth !== undefined) {
headers["Authorization"] = "Basic " + basicAuth;
}
Expand Down
8 changes: 8 additions & 0 deletions src/OidcClientSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ export interface OidcClientSettings {
* Only scopes in this list will be passed in the token refresh request.
*/
refreshTokenAllowedScope?: string | undefined;

/**
* enable DPoP
*/
enable_dpop?: boolean;
}

/**
Expand All @@ -154,6 +159,7 @@ export class OidcClientSettingsStore {
public readonly redirect_uri: string;
public readonly post_logout_redirect_uri: string | undefined;
public readonly client_authentication: "client_secret_basic" | "client_secret_post";
public readonly enable_dpop?: boolean;

// optional protocol params
public readonly prompt: string | undefined;
Expand Down Expand Up @@ -209,6 +215,7 @@ export class OidcClientSettingsStore {
// extra query params
extraQueryParams = {},
extraTokenParams = {},
enable_dpop = false,
}: OidcClientSettings) {

this.authority = authority;
Expand Down Expand Up @@ -272,5 +279,6 @@ export class OidcClientSettingsStore {

this.extraQueryParams = extraQueryParams;
this.extraTokenParams = extraTokenParams;
this.enable_dpop = enable_dpop;
}
}
28 changes: 27 additions & 1 deletion src/TokenClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ import { CryptoUtils, Logger } from "./utils";
import { JsonService } from "./JsonService";
import type { MetadataService } from "./MetadataService";
import type { OidcClientSettingsStore } from "./OidcClientSettings";
import DPoP, { generateKeyPair } from "dpop";

// Tempeory solution, store dpop keypair in memory
const DpopKeypair = {
publicKey: null,
privateKey: null,
};
export { DpopKeypair } ;

/**
* @internal
Expand Down Expand Up @@ -55,6 +63,22 @@ export interface RevokeArgs {
token_type_hint?: "access_token" | "refresh_token";
}

const buildDPoPHeader = async (url: string, method: string, token: any) =>{
const keypair = await generateKeyPair("ES256");
const dpop = await DPoP(
keypair as any,
url,
method,
undefined,
token,
);

DpopKeypair.publicKey = keypair.publicKey as any;
DpopKeypair.privateKey = keypair.privateKey as any;

return dpop;
};

/**
* @internal
*/
Expand Down Expand Up @@ -113,7 +137,9 @@ export class TokenClient {
const url = await this._metadataService.getTokenEndpoint(false);
logger.debug("got token endpoint");

const response = await this._jsonService.postForm(url, { body: params, basicAuth, initCredentials: this._settings.fetchRequestCredentials });
const dpopHeader = this._settings.enable_dpop ? await buildDPoPHeader(url, "POST", undefined) : undefined;

const response = await this._jsonService.postForm(url, { body: params, basicAuth, initCredentials: this._settings.fetchRequestCredentials, dpopHeader });
logger.debug("got response");

return response;
Expand Down
1 change: 1 addition & 0 deletions src/UserManagerSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ export interface UserManagerSettings extends OidcClientSettings {
* E.g. `userStore: new WebStorageStateStore({ store: window.localStorage })`
*/
userStore?: WebStorageStateStore;
enable_dpop?: boolean;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ export { UserManagerSettingsStore } from "./UserManagerSettings";
export type { UserManagerSettings } from "./UserManagerSettings";
export { Version } from "./Version";
export { WebStorageStateStore } from "./WebStorageStateStore";
export { DpopKeypair } from "./TokenClient";

0 comments on commit 2f9c7ab

Please sign in to comment.