Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: token-set #48

Merged
merged 2 commits into from
Oct 18, 2021
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
64 changes: 64 additions & 0 deletions packages/client/src/token-set.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { SignJWT } from 'jose/jwt/sign';
import { generateKeyPair } from 'jose/util/generate_key_pair';

import TokenSet from './token-set';
import { nowRoundToSec } from './utils';

describe('TokenSet', () => {
test('sets the expire_at automatically from expires_in', () => {
const ts = new TokenSet({
access_token: 'at',
expires_in: 300,
refresh_token: 'rt',
id_token: 'it',
});

expect(ts).toHaveProperty('expiresAt', nowRoundToSec() + 300);
expect(ts).toHaveProperty('expiresIn', 300);
expect(ts.expired()).toBeFalsy();
});

test('expired token sets expires_in to -30', () => {
const ts = new TokenSet({
access_token: 'at',
expires_in: -30,
refresh_token: 'rt',
id_token: 'it',
});

expect(ts).toHaveProperty('expiresAt', nowRoundToSec() - 30);
expect(ts).toHaveProperty('expiresIn', 0);
expect(ts.expired()).toBeTruthy();
});

test('provides a #claims getter', async () => {
const ts = new TokenSet({
access_token: 'at',
expires_in: -30,
refresh_token: 'rt',
id_token: await new SignJWT({})
.setProtectedHeader({ alg: 'RS256' })
.setAudience('foo')
.setSubject('foz')
.setIssuer('logto')
.setIssuedAt()
.setExpirationTime('2h')
.sign((await generateKeyPair('RS256')).privateKey),
});

expect(ts.claims().aud).toEqual('foo');
expect(ts.claims().sub).toEqual('foz');
expect(ts.claims().iss).toEqual('logto');
});

test('#claims throws if no id_token is present', () => {
const ts = new TokenSet({
access_token: 'at',
expires_in: 300,
refresh_token: 'rt',
id_token: '',
});

expect(() => ts.claims()).toThrowError('id_token not present in TokenSet');
});
});
35 changes: 35 additions & 0 deletions packages/client/src/token-set.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { TokenSetParameters } from './grant-token';
import { decodeToken, IDToken, nowRoundToSec } from './utils';

export default class TokenSet {
public accessToken: string;
public idToken: string;
public refreshToken: string;
public expiresAt = 0;
constructor(tokenSet: TokenSetParameters) {
this.accessToken = tokenSet.access_token;
this.expiresIn = tokenSet.expires_in;
this.idToken = tokenSet.id_token;
this.refreshToken = tokenSet.refresh_token;
}

get expiresIn(): number {
return Math.max(this.expiresAt - nowRoundToSec(), 0);
}

set expiresIn(value: number) {
this.expiresAt = nowRoundToSec() + value;
}

public expired(): boolean {
return this.expiresIn === 0;
}

public claims(): IDToken {
if (!this.idToken) {
throw new TypeError('id_token not present in TokenSet');
}

return decodeToken(this.idToken);
}
}
2 changes: 2 additions & 0 deletions packages/client/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,5 @@ export const decodeToken = (token: string): IDToken => {
throw new Error('invalid token: JSON parse failed');
}
};

export const nowRoundToSec = () => Math.floor(Date.now() / 1000);