Merge pull request #3409 from bleathem/oidc-provider
Fixed the OIDC token refresh
This commit is contained in:
@@ -54,9 +54,11 @@ describe('OidcAuthProvider', () => {
|
||||
.get('/.well-known/openid-configuration')
|
||||
.reply(200, issuerMetadata);
|
||||
const provider = new OidcAuthProvider(clientMetadata);
|
||||
const strategy = ((await provider._strategy) as any) as {
|
||||
_client: ClientMetadata;
|
||||
_issuer: IssuerMetadata;
|
||||
const { strategy } = ((await (provider as any).implementation) as any) as {
|
||||
strategy: {
|
||||
_client: ClientMetadata;
|
||||
_issuer: IssuerMetadata;
|
||||
};
|
||||
};
|
||||
// Assert that the expected request to the metadaurl was made.
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
@@ -89,27 +91,38 @@ describe('OidcAuthProvider', () => {
|
||||
const provider = new OidcAuthProvider(clientMetadata);
|
||||
const req = {
|
||||
method: 'GET',
|
||||
url: '/?code=test2',
|
||||
url: 'https://oidc.test/?code=test2',
|
||||
session: ({ 'oidc:oidc.test': 'test' } as any) as Session,
|
||||
} as express.Request;
|
||||
await provider.handler(req);
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
|
||||
const options = {
|
||||
globalConfig: {
|
||||
appUrl: 'https://oidc.test',
|
||||
baseUrl: 'https://oidc.test',
|
||||
},
|
||||
config: ({
|
||||
keys: jest.fn(() => ['test']),
|
||||
getConfig: jest.fn(() => ({ getString: () => '' })),
|
||||
} as any) as Config,
|
||||
} as AuthProviderFactoryOptions;
|
||||
|
||||
it('createOidcProvider', () => {
|
||||
it('createOidcProvider', async () => {
|
||||
const scope = nock('https://oidc.test')
|
||||
.get('/.well-known/openid-configuration')
|
||||
.reply(200, issuerMetadata);
|
||||
const options = {
|
||||
globalConfig: {
|
||||
appUrl: 'https://oidc.test',
|
||||
baseUrl: 'https://oidc.test',
|
||||
},
|
||||
config: ({
|
||||
keys: jest.fn(() => ['test']),
|
||||
getConfig: jest.fn(() => ({
|
||||
getString: (key: string) => {
|
||||
const conf = {
|
||||
...clientMetadata,
|
||||
metadataUrl: 'https://oidc.test/.well-known/openid-configuration',
|
||||
} as any;
|
||||
return conf[key] as string;
|
||||
},
|
||||
})),
|
||||
} as any) as Config,
|
||||
} as AuthProviderFactoryOptions;
|
||||
const provider = createOidcProvider(options) as OAuthAdapter;
|
||||
console.log(provider);
|
||||
expect(provider.start).toBeDefined();
|
||||
await new Promise(resolve => process.nextTick(resolve)); // advance a tick to give nock a chance to intercept the request
|
||||
expect(scope.isDone()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,10 +33,8 @@ import {
|
||||
OAuthRefreshRequest,
|
||||
} from '../../lib/oauth';
|
||||
import {
|
||||
executeFetchUserProfileStrategy,
|
||||
executeFrameHandlerStrategy,
|
||||
executeRedirectStrategy,
|
||||
executeRefreshTokenStrategy,
|
||||
PassportDoneCallback,
|
||||
} from '../../lib/passport';
|
||||
import { RedirectInfo, AuthProviderFactory, ProfileInfo } from '../types';
|
||||
@@ -45,20 +43,25 @@ type PrivateInfo = {
|
||||
refreshToken: string;
|
||||
};
|
||||
|
||||
type OidcImpl = {
|
||||
strategy: OidcStrategy<UserinfoResponse, Client>;
|
||||
client: Client;
|
||||
};
|
||||
|
||||
export type OidcAuthProviderOptions = OAuthProviderOptions & {
|
||||
metadataUrl: string;
|
||||
tokenSignedResponseAlg?: string;
|
||||
};
|
||||
|
||||
export class OidcAuthProvider implements OAuthHandlers {
|
||||
readonly _strategy: Promise<OidcStrategy<UserinfoResponse, Client>>;
|
||||
private readonly implementation: Promise<OidcImpl>;
|
||||
|
||||
constructor(options: OidcAuthProviderOptions) {
|
||||
this._strategy = this.setupStrategy(options);
|
||||
this.implementation = this.setupStrategy(options);
|
||||
}
|
||||
|
||||
async start(req: OAuthStartRequest): Promise<RedirectInfo> {
|
||||
const strategy = await this._strategy;
|
||||
const { strategy } = await this.implementation;
|
||||
return await executeRedirectStrategy(req, strategy, {
|
||||
accessType: 'offline',
|
||||
prompt: 'none',
|
||||
@@ -70,7 +73,7 @@ export class OidcAuthProvider implements OAuthHandlers {
|
||||
async handler(
|
||||
req: express.Request,
|
||||
): Promise<{ response: OAuthResponse; refreshToken: string }> {
|
||||
const strategy = await this._strategy;
|
||||
const { strategy } = await this.implementation;
|
||||
const { response, privateInfo } = await executeFrameHandlerStrategy<
|
||||
OAuthResponse,
|
||||
PrivateInfo
|
||||
@@ -83,31 +86,20 @@ export class OidcAuthProvider implements OAuthHandlers {
|
||||
}
|
||||
|
||||
async refresh(req: OAuthRefreshRequest): Promise<OAuthResponse> {
|
||||
const strategy = await this._strategy;
|
||||
const refreshTokenResponse = await executeRefreshTokenStrategy(
|
||||
strategy,
|
||||
req.refreshToken,
|
||||
req.scope,
|
||||
);
|
||||
const {
|
||||
accessToken,
|
||||
params,
|
||||
refreshToken: updatedRefreshToken,
|
||||
} = refreshTokenResponse;
|
||||
|
||||
const profile = await executeFetchUserProfileStrategy(
|
||||
strategy,
|
||||
accessToken,
|
||||
params.id_token,
|
||||
);
|
||||
const { client } = await this.implementation;
|
||||
const tokenset = await client.refresh(req.refreshToken);
|
||||
if (!tokenset.access_token) {
|
||||
throw new Error('Refresh failed');
|
||||
}
|
||||
const profile = await client.userinfo(tokenset.access_token);
|
||||
|
||||
return this.populateIdentity({
|
||||
providerInfo: {
|
||||
accessToken,
|
||||
refreshToken: updatedRefreshToken,
|
||||
idToken: params.id_token,
|
||||
expiresInSeconds: params.expires_in,
|
||||
scope: params.scope,
|
||||
accessToken: tokenset.access_token,
|
||||
refreshToken: tokenset.refresh_token,
|
||||
expiresInSeconds: tokenset.expires_in,
|
||||
idToken: tokenset.id_token,
|
||||
scope: tokenset.scope || '',
|
||||
},
|
||||
profile,
|
||||
});
|
||||
@@ -115,7 +107,7 @@ export class OidcAuthProvider implements OAuthHandlers {
|
||||
|
||||
private async setupStrategy(
|
||||
options: OidcAuthProviderOptions,
|
||||
): Promise<OidcStrategy<UserinfoResponse, Client>> {
|
||||
): Promise<OidcImpl> {
|
||||
const issuer = await Issuer.discover(options.metadataUrl);
|
||||
const client = new issuer.Client({
|
||||
client_id: options.clientId,
|
||||
@@ -159,7 +151,7 @@ export class OidcAuthProvider implements OAuthHandlers {
|
||||
},
|
||||
);
|
||||
strategy.error = console.error;
|
||||
return strategy;
|
||||
return { strategy, client };
|
||||
}
|
||||
|
||||
// Use this function to grab the user profile info from the token
|
||||
|
||||
Reference in New Issue
Block a user