Skip to content
Open
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
29 changes: 26 additions & 3 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
IntrospectionRequest,
IntrospectionResponse,
PasswordRequest,
JwtBearerRequest,
OAuth2TokenTypeHint,
RefreshRequest,
RevocationRequest,
Expand Down Expand Up @@ -42,6 +43,15 @@ type PasswordParams = {

}

type JwtBearerParams = {
/**
* The JSON Web Token to use for the JWT Bearer token request.
*/
assertion: string;

scope?: string[];
}

/**
* Extra options that may be passed to refresh()
*/
Expand Down Expand Up @@ -79,8 +89,8 @@ export interface ClientSettings {
* OAuth2 clientSecret
*
* This is required when using the 'client_secret_basic' authenticationMethod
* for the client_credentials and password flows, but not authorization_code
* or implicit.
* for the client_credentials and password flows, but not authorization_code,
* implicit or JWT Bearer.
*/
clientSecret?: string;

Expand Down Expand Up @@ -225,6 +235,19 @@ export class OAuth2Client {

}

/**
* Retrieves an OAuth2 token using the 'urn:ietf:params:oauth:grant-type:jwt-bearer' grant.
*/
async jwtBearer(params: JwtBearerParams): Promise<OAuth2Token> {

const body: JwtBearerRequest = {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: params.assertion,
scope: params.scope?.join(' '),
};
return this.tokenResponseToOAuth2Token(this.request('tokenEndpoint', body));
}

/**
* Returns the helper object for the `authorization_code` grant.
*/
Expand Down Expand Up @@ -366,7 +389,7 @@ export class OAuth2Client {
/**
* Does a HTTP request on the 'token' endpoint.
*/
async request(endpoint: 'tokenEndpoint', body: RefreshRequest | ClientCredentialsRequest | PasswordRequest | AuthorizationCodeRequest): Promise<TokenResponse>;
async request(endpoint: 'tokenEndpoint', body: RefreshRequest | ClientCredentialsRequest | PasswordRequest | JwtBearerRequest | AuthorizationCodeRequest): Promise<TokenResponse>;
async request(endpoint: 'introspectionEndpoint', body: IntrospectionRequest): Promise<IntrospectionResponse>;
async request(endpoint: 'revocationEndpoint', body: RevocationRequest): Promise<void>;
async request(endpoint: OAuth2Endpoint, body: Record<string, any>): Promise<unknown> {
Expand Down
9 changes: 9 additions & 0 deletions src/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ export type PasswordRequest = {
resource?: string | string[];
}

/**
* JWT Bearer Grant Type request body
*/
export type JwtBearerRequest = {
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer';
assertion: string;
scope?: string;
}

export type AuthorizationCodeRequest = {
grant_type: 'authorization_code';
code: string;
Expand Down
39 changes: 39 additions & 0 deletions test/jwt-bearer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { testServer } from './test-server';
import { OAuth2Client } from '../src';
import { expect } from 'chai';

describe('jwt-bearer', () => {

it('should work with client_secret_post', async () => {

const server = testServer();

const client = new OAuth2Client({
server: server.url,
tokenEndpoint: '/token',
clientId: 'test-client-id',
});

const result = await client.jwtBearer({
assertion: 'foobar',
scope: ['hello', 'world']
});

expect(result.accessToken).to.equal('access_token_000');
expect(result.refreshToken).to.equal('refresh_token_000');
expect(result.expiresAt).to.be.lessThanOrEqual(Date.now() + 3600_000);
expect(result.expiresAt).to.be.greaterThanOrEqual(Date.now() + 3500_000);

const request = server.lastRequest();
expect(request.headers.get('Authorization')).to.be.null;
expect(request.headers.get('Accept')).to.equal('application/json');

expect(request.body).to.eql({
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
assertion: 'foobar',
scope: 'hello world',
client_id: 'test-client-id'
});
});

});
Loading