@logto/js 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +2 -0
- package/lib/index.js +1 -0
- package/lib/utils/access-token.cjs +40 -0
- package/lib/utils/access-token.d.ts +11 -0
- package/lib/utils/access-token.js +38 -0
- package/lib/utils/access-token.test.d.ts +1 -0
- package/lib/utils/errors.cjs +1 -0
- package/lib/utils/errors.d.ts +1 -0
- package/lib/utils/errors.js +1 -0
- package/lib/utils/index.d.ts +1 -0
- package/package.json +1 -1
package/lib/index.cjs
CHANGED
|
@@ -9,6 +9,7 @@ var userInfo = require('./core/user-info.cjs');
|
|
|
9
9
|
var callbackUri = require('./utils/callback-uri.cjs');
|
|
10
10
|
var errors = require('./utils/errors.cjs');
|
|
11
11
|
var idToken = require('./utils/id-token.cjs');
|
|
12
|
+
var accessToken = require('./utils/access-token.cjs');
|
|
12
13
|
var scopes = require('./utils/scopes.cjs');
|
|
13
14
|
var arbitraryObject = require('./utils/arbitrary-object.cjs');
|
|
14
15
|
var index = require('./consts/index.cjs');
|
|
@@ -31,6 +32,7 @@ exports.OidcError = errors.OidcError;
|
|
|
31
32
|
exports.isLogtoRequestError = errors.isLogtoRequestError;
|
|
32
33
|
exports.decodeIdToken = idToken.decodeIdToken;
|
|
33
34
|
exports.verifyIdToken = idToken.verifyIdToken;
|
|
35
|
+
exports.decodeAccessToken = accessToken.decodeAccessToken;
|
|
34
36
|
exports.withDefaultScopes = scopes.withDefaultScopes;
|
|
35
37
|
exports.isArbitraryObject = arbitraryObject.isArbitraryObject;
|
|
36
38
|
exports.ContentType = index.ContentType;
|
package/lib/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export { fetchUserInfo } from './core/user-info.js';
|
|
|
7
7
|
export { parseUriParameters, verifyAndParseCodeFromCallbackUri } from './utils/callback-uri.js';
|
|
8
8
|
export { LogtoError, LogtoRequestError, OidcError, isLogtoRequestError } from './utils/errors.js';
|
|
9
9
|
export { decodeIdToken, verifyIdToken } from './utils/id-token.js';
|
|
10
|
+
export { decodeAccessToken } from './utils/access-token.js';
|
|
10
11
|
export { withDefaultScopes } from './utils/scopes.js';
|
|
11
12
|
export { isArbitraryObject } from './utils/arbitrary-object.js';
|
|
12
13
|
export { ContentType, Prompt, QueryKey, ReservedScope, TokenGrantType, UserScope } from './consts/index.js';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var essentials = require('@silverhand/essentials');
|
|
4
|
+
var arbitraryObject = require('./arbitrary-object.cjs');
|
|
5
|
+
var errors = require('./errors.cjs');
|
|
6
|
+
|
|
7
|
+
// https://docs.logto.io/docs/recipes/protect-your-api/
|
|
8
|
+
function assertAccessTokenClaims(data) {
|
|
9
|
+
if (!arbitraryObject.isArbitraryObject(data)) {
|
|
10
|
+
throw new TypeError('AccessToken is expected to be an object');
|
|
11
|
+
}
|
|
12
|
+
for (const key of ['jti', 'iss', 'sub', 'aud', 'client_id', 'scope']) {
|
|
13
|
+
if (data[key] === undefined) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (typeof data[key] !== 'string' && data[key] !== null) {
|
|
17
|
+
throw new TypeError(`At path: AccessToken.${key}: expected null or a string`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for (const key of ['exp', 'iat']) {
|
|
21
|
+
if (data[key] === undefined) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (typeof data[key] !== 'number' && data[key] !== null) {
|
|
25
|
+
throw new TypeError(`At path: AccessToken.${key}: expected null or a number`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const decodeAccessToken = (accessToken) => {
|
|
30
|
+
const { 1: encodedPayload } = accessToken.split('.');
|
|
31
|
+
if (!encodedPayload) {
|
|
32
|
+
throw new errors.LogtoError('access_token.invalid_token');
|
|
33
|
+
}
|
|
34
|
+
const json = essentials.urlSafeBase64.decode(encodedPayload);
|
|
35
|
+
const accessTokenClaims = JSON.parse(json);
|
|
36
|
+
assertAccessTokenClaims(accessTokenClaims);
|
|
37
|
+
return accessTokenClaims;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
exports.decodeAccessToken = decodeAccessToken;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type AccessTokenClaims = {
|
|
2
|
+
jti?: string;
|
|
3
|
+
iss?: string;
|
|
4
|
+
sub?: string;
|
|
5
|
+
aud?: string;
|
|
6
|
+
exp?: number;
|
|
7
|
+
iat?: number;
|
|
8
|
+
client_id?: string;
|
|
9
|
+
scope?: string;
|
|
10
|
+
} & Record<string, unknown>;
|
|
11
|
+
export declare const decodeAccessToken: (accessToken: string) => AccessTokenClaims;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { urlSafeBase64 } from '@silverhand/essentials';
|
|
2
|
+
import { isArbitraryObject } from './arbitrary-object.js';
|
|
3
|
+
import { LogtoError } from './errors.js';
|
|
4
|
+
|
|
5
|
+
// https://docs.logto.io/docs/recipes/protect-your-api/
|
|
6
|
+
function assertAccessTokenClaims(data) {
|
|
7
|
+
if (!isArbitraryObject(data)) {
|
|
8
|
+
throw new TypeError('AccessToken is expected to be an object');
|
|
9
|
+
}
|
|
10
|
+
for (const key of ['jti', 'iss', 'sub', 'aud', 'client_id', 'scope']) {
|
|
11
|
+
if (data[key] === undefined) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (typeof data[key] !== 'string' && data[key] !== null) {
|
|
15
|
+
throw new TypeError(`At path: AccessToken.${key}: expected null or a string`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
for (const key of ['exp', 'iat']) {
|
|
19
|
+
if (data[key] === undefined) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (typeof data[key] !== 'number' && data[key] !== null) {
|
|
23
|
+
throw new TypeError(`At path: AccessToken.${key}: expected null or a number`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const decodeAccessToken = (accessToken) => {
|
|
28
|
+
const { 1: encodedPayload } = accessToken.split('.');
|
|
29
|
+
if (!encodedPayload) {
|
|
30
|
+
throw new LogtoError('access_token.invalid_token');
|
|
31
|
+
}
|
|
32
|
+
const json = urlSafeBase64.decode(encodedPayload);
|
|
33
|
+
const accessTokenClaims = JSON.parse(json);
|
|
34
|
+
assertAccessTokenClaims(accessTokenClaims);
|
|
35
|
+
return accessTokenClaims;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { decodeAccessToken };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/utils/errors.cjs
CHANGED
|
@@ -12,6 +12,7 @@ const logtoErrorCodes = Object.freeze({
|
|
|
12
12
|
'callback_uri_verification.missing_code': 'Missing code in the callback URI',
|
|
13
13
|
crypto_subtle_unavailable: 'Crypto.subtle is unavailable in insecure contexts (non-HTTPS).',
|
|
14
14
|
unexpected_response_error: 'Unexpected response error from the server.',
|
|
15
|
+
'access_token.invalid_token': 'Invalid access token',
|
|
15
16
|
});
|
|
16
17
|
class LogtoError extends Error {
|
|
17
18
|
constructor(code, data) {
|
package/lib/utils/errors.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare const logtoErrorCodes: Readonly<{
|
|
|
8
8
|
'callback_uri_verification.missing_code': "Missing code in the callback URI";
|
|
9
9
|
crypto_subtle_unavailable: "Crypto.subtle is unavailable in insecure contexts (non-HTTPS).";
|
|
10
10
|
unexpected_response_error: "Unexpected response error from the server.";
|
|
11
|
+
'access_token.invalid_token': "Invalid access token";
|
|
11
12
|
}>;
|
|
12
13
|
export type LogtoErrorCode = keyof typeof logtoErrorCodes;
|
|
13
14
|
export declare class LogtoError extends Error {
|
package/lib/utils/errors.js
CHANGED
|
@@ -10,6 +10,7 @@ const logtoErrorCodes = Object.freeze({
|
|
|
10
10
|
'callback_uri_verification.missing_code': 'Missing code in the callback URI',
|
|
11
11
|
crypto_subtle_unavailable: 'Crypto.subtle is unavailable in insecure contexts (non-HTTPS).',
|
|
12
12
|
unexpected_response_error: 'Unexpected response error from the server.',
|
|
13
|
+
'access_token.invalid_token': 'Invalid access token',
|
|
13
14
|
});
|
|
14
15
|
class LogtoError extends Error {
|
|
15
16
|
constructor(code, data) {
|
package/lib/utils/index.d.ts
CHANGED