@logto/js 1.1.2 → 2.0.1
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/consts/{index.mjs → index.cjs} +13 -11
- package/lib/consts/index.js +11 -13
- package/lib/core/fetch-token.cjs +47 -0
- package/lib/core/fetch-token.d.ts +1 -1
- package/lib/core/fetch-token.js +18 -25
- package/lib/core/index.d.ts +6 -6
- package/lib/core/oidc-config.cjs +13 -0
- package/lib/core/oidc-config.d.ts +1 -1
- package/lib/core/oidc-config.js +3 -10
- package/lib/core/revoke.cjs +14 -0
- package/lib/core/revoke.d.ts +1 -1
- package/lib/core/revoke.js +5 -7
- package/lib/core/sign-in.cjs +31 -0
- package/lib/core/sign-in.d.ts +2 -2
- package/lib/core/sign-in.js +16 -19
- package/lib/core/sign-out.cjs +13 -0
- package/lib/core/sign-out.js +4 -6
- package/lib/core/{user-info.mjs → user-info.cjs} +3 -1
- package/lib/core/user-info.d.ts +1 -1
- package/lib/core/user-info.js +1 -3
- package/lib/index.cjs +56 -0
- package/lib/index.d.ts +4 -4
- package/lib/index.js +12 -56
- package/lib/utils/{arbitrary-object.mjs → arbitrary-object.cjs} +3 -1
- package/lib/utils/arbitrary-object.js +1 -3
- package/lib/utils/callback-uri.cjs +36 -0
- package/lib/utils/callback-uri.js +13 -16
- package/lib/utils/errors.cjs +45 -0
- package/lib/utils/errors.d.ts +8 -13
- package/lib/utils/errors.js +11 -33
- package/lib/utils/{id-token.mjs → id-token.cjs} +13 -10
- package/lib/utils/id-token.js +10 -13
- package/lib/utils/index.d.ts +5 -5
- package/lib/utils/scopes.cjs +15 -0
- package/lib/utils/scopes.js +4 -6
- package/package.json +23 -27
- package/lib/core/fetch-token.mjs +0 -40
- package/lib/core/oidc-config.mjs +0 -6
- package/lib/core/revoke.mjs +0 -12
- package/lib/core/sign-in.mjs +0 -30
- package/lib/core/sign-out.mjs +0 -11
- package/lib/index.mjs +0 -12
- package/lib/utils/callback-uri.mjs +0 -33
- package/lib/utils/errors.mjs +0 -53
- package/lib/utils/scopes.mjs +0 -13
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var essentials = require('@silverhand/essentials');
|
|
4
|
+
var index = require('../consts/index.cjs');
|
|
5
|
+
var errors = require('./errors.cjs');
|
|
6
|
+
|
|
7
|
+
const parseUriParameters = (uri) => {
|
|
8
|
+
const [, queryString = ''] = uri.split('?');
|
|
9
|
+
return new URLSearchParams(queryString);
|
|
10
|
+
};
|
|
11
|
+
const verifyAndParseCodeFromCallbackUri = (callbackUri, redirectUri, state) => {
|
|
12
|
+
if (!callbackUri.startsWith(redirectUri)) {
|
|
13
|
+
throw new errors.LogtoError('callback_uri_verification.redirect_uri_mismatched');
|
|
14
|
+
}
|
|
15
|
+
const uriParameters = parseUriParameters(callbackUri);
|
|
16
|
+
const error = essentials.conditional(uriParameters.get(index.QueryKey.Error));
|
|
17
|
+
const errorDescription = essentials.conditional(uriParameters.get(index.QueryKey.ErrorDescription));
|
|
18
|
+
if (error) {
|
|
19
|
+
throw new errors.LogtoError('callback_uri_verification.error_found', new errors.OidcError(error, errorDescription));
|
|
20
|
+
}
|
|
21
|
+
const stateFromCallbackUri = uriParameters.get(index.QueryKey.State);
|
|
22
|
+
if (!stateFromCallbackUri) {
|
|
23
|
+
throw new errors.LogtoError('callback_uri_verification.missing_state');
|
|
24
|
+
}
|
|
25
|
+
if (stateFromCallbackUri !== state) {
|
|
26
|
+
throw new errors.LogtoError('callback_uri_verification.state_mismatched');
|
|
27
|
+
}
|
|
28
|
+
const code = uriParameters.get(index.QueryKey.Code);
|
|
29
|
+
if (!code) {
|
|
30
|
+
throw new errors.LogtoError('callback_uri_verification.missing_code');
|
|
31
|
+
}
|
|
32
|
+
return code;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
exports.parseUriParameters = parseUriParameters;
|
|
36
|
+
exports.verifyAndParseCodeFromCallbackUri = verifyAndParseCodeFromCallbackUri;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var index = require('../consts/index.js');
|
|
5
|
-
var errors = require('./errors.js');
|
|
1
|
+
import { conditional } from '@silverhand/essentials';
|
|
2
|
+
import { QueryKey } from '../consts/index.js';
|
|
3
|
+
import { LogtoError, OidcError } from './errors.js';
|
|
6
4
|
|
|
7
5
|
const parseUriParameters = (uri) => {
|
|
8
6
|
const [, queryString = ''] = uri.split('?');
|
|
@@ -10,27 +8,26 @@ const parseUriParameters = (uri) => {
|
|
|
10
8
|
};
|
|
11
9
|
const verifyAndParseCodeFromCallbackUri = (callbackUri, redirectUri, state) => {
|
|
12
10
|
if (!callbackUri.startsWith(redirectUri)) {
|
|
13
|
-
throw new
|
|
11
|
+
throw new LogtoError('callback_uri_verification.redirect_uri_mismatched');
|
|
14
12
|
}
|
|
15
13
|
const uriParameters = parseUriParameters(callbackUri);
|
|
16
|
-
const error =
|
|
17
|
-
const errorDescription =
|
|
14
|
+
const error = conditional(uriParameters.get(QueryKey.Error));
|
|
15
|
+
const errorDescription = conditional(uriParameters.get(QueryKey.ErrorDescription));
|
|
18
16
|
if (error) {
|
|
19
|
-
throw new
|
|
17
|
+
throw new LogtoError('callback_uri_verification.error_found', new OidcError(error, errorDescription));
|
|
20
18
|
}
|
|
21
|
-
const stateFromCallbackUri = uriParameters.get(
|
|
19
|
+
const stateFromCallbackUri = uriParameters.get(QueryKey.State);
|
|
22
20
|
if (!stateFromCallbackUri) {
|
|
23
|
-
throw new
|
|
21
|
+
throw new LogtoError('callback_uri_verification.missing_state');
|
|
24
22
|
}
|
|
25
23
|
if (stateFromCallbackUri !== state) {
|
|
26
|
-
throw new
|
|
24
|
+
throw new LogtoError('callback_uri_verification.state_mismatched');
|
|
27
25
|
}
|
|
28
|
-
const code = uriParameters.get(
|
|
26
|
+
const code = uriParameters.get(QueryKey.Code);
|
|
29
27
|
if (!code) {
|
|
30
|
-
throw new
|
|
28
|
+
throw new LogtoError('callback_uri_verification.missing_code');
|
|
31
29
|
}
|
|
32
30
|
return code;
|
|
33
31
|
};
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
exports.verifyAndParseCodeFromCallbackUri = verifyAndParseCodeFromCallbackUri;
|
|
33
|
+
export { parseUriParameters, verifyAndParseCodeFromCallbackUri };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var arbitraryObject = require('./arbitrary-object.cjs');
|
|
4
|
+
|
|
5
|
+
const logtoErrorCodes = Object.freeze({
|
|
6
|
+
'id_token.invalid_iat': 'Invalid issued at time in the ID token',
|
|
7
|
+
'id_token.invalid_token': 'Invalid ID token',
|
|
8
|
+
'callback_uri_verification.redirect_uri_mismatched': 'The callback URI mismatches the redirect URI.',
|
|
9
|
+
'callback_uri_verification.error_found': 'Error found in the callback URI',
|
|
10
|
+
'callback_uri_verification.missing_state': 'Missing state in the callback URI',
|
|
11
|
+
'callback_uri_verification.state_mismatched': 'State mismatched in the callback URI',
|
|
12
|
+
'callback_uri_verification.missing_code': 'Missing code in the callback URI',
|
|
13
|
+
crypto_subtle_unavailable: 'Crypto.subtle is unavailable in insecure contexts (non-HTTPS).',
|
|
14
|
+
unexpected_response_error: 'Unexpected response error from the server.',
|
|
15
|
+
});
|
|
16
|
+
class LogtoError extends Error {
|
|
17
|
+
constructor(code, data) {
|
|
18
|
+
super(logtoErrorCodes[code]);
|
|
19
|
+
this.code = code;
|
|
20
|
+
this.data = data;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const isLogtoRequestError = (data) => {
|
|
24
|
+
if (!arbitraryObject.isArbitraryObject(data)) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return typeof data.code === 'string' && typeof data.message === 'string';
|
|
28
|
+
};
|
|
29
|
+
class LogtoRequestError extends Error {
|
|
30
|
+
constructor(code, message) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.code = code;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
class OidcError {
|
|
36
|
+
constructor(error, errorDescription) {
|
|
37
|
+
this.error = error;
|
|
38
|
+
this.errorDescription = errorDescription;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
exports.LogtoError = LogtoError;
|
|
43
|
+
exports.LogtoRequestError = LogtoRequestError;
|
|
44
|
+
exports.OidcError = OidcError;
|
|
45
|
+
exports.isLogtoRequestError = isLogtoRequestError;
|
package/lib/utils/errors.d.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
import type { NormalizeKeyPaths } from '@silverhand/essentials';
|
|
2
1
|
declare const logtoErrorCodes: Readonly<{
|
|
3
|
-
id_token:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
callback_uri_verification:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
missing_state: string;
|
|
11
|
-
state_mismatched: string;
|
|
12
|
-
missing_code: string;
|
|
13
|
-
};
|
|
2
|
+
'id_token.invalid_iat': "Invalid issued at time in the ID token";
|
|
3
|
+
'id_token.invalid_token': "Invalid ID token";
|
|
4
|
+
'callback_uri_verification.redirect_uri_mismatched': "The callback URI mismatches the redirect URI.";
|
|
5
|
+
'callback_uri_verification.error_found': "Error found in the callback URI";
|
|
6
|
+
'callback_uri_verification.missing_state': "Missing state in the callback URI";
|
|
7
|
+
'callback_uri_verification.state_mismatched': "State mismatched in the callback URI";
|
|
8
|
+
'callback_uri_verification.missing_code': "Missing code in the callback URI";
|
|
14
9
|
crypto_subtle_unavailable: "Crypto.subtle is unavailable in insecure contexts (non-HTTPS).";
|
|
15
10
|
unexpected_response_error: "Unexpected response error from the server.";
|
|
16
11
|
}>;
|
|
17
|
-
export type LogtoErrorCode =
|
|
12
|
+
export type LogtoErrorCode = keyof typeof logtoErrorCodes;
|
|
18
13
|
export declare class LogtoError extends Error {
|
|
19
14
|
code: LogtoErrorCode;
|
|
20
15
|
data: unknown;
|
package/lib/utils/errors.js
CHANGED
|
@@ -1,44 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var get = require('lodash.get');
|
|
4
|
-
var arbitraryObject = require('./arbitrary-object.js');
|
|
5
|
-
|
|
6
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
-
|
|
8
|
-
var get__default = /*#__PURE__*/_interopDefault(get);
|
|
1
|
+
import { isArbitraryObject } from './arbitrary-object.js';
|
|
9
2
|
|
|
10
3
|
const logtoErrorCodes = Object.freeze({
|
|
11
|
-
id_token:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
callback_uri_verification:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
missing_state: 'Missing state in the callback URI',
|
|
19
|
-
state_mismatched: 'State mismatched in the callback URI',
|
|
20
|
-
missing_code: 'Missing code in the callback URI',
|
|
21
|
-
},
|
|
4
|
+
'id_token.invalid_iat': 'Invalid issued at time in the ID token',
|
|
5
|
+
'id_token.invalid_token': 'Invalid ID token',
|
|
6
|
+
'callback_uri_verification.redirect_uri_mismatched': 'The callback URI mismatches the redirect URI.',
|
|
7
|
+
'callback_uri_verification.error_found': 'Error found in the callback URI',
|
|
8
|
+
'callback_uri_verification.missing_state': 'Missing state in the callback URI',
|
|
9
|
+
'callback_uri_verification.state_mismatched': 'State mismatched in the callback URI',
|
|
10
|
+
'callback_uri_verification.missing_code': 'Missing code in the callback URI',
|
|
22
11
|
crypto_subtle_unavailable: 'Crypto.subtle is unavailable in insecure contexts (non-HTTPS).',
|
|
23
12
|
unexpected_response_error: 'Unexpected response error from the server.',
|
|
24
13
|
});
|
|
25
|
-
const getMessageByErrorCode = (errorCode) => {
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
27
|
-
const message = get__default.default(logtoErrorCodes, errorCode);
|
|
28
|
-
if (typeof message === 'string') {
|
|
29
|
-
return message;
|
|
30
|
-
}
|
|
31
|
-
return errorCode;
|
|
32
|
-
};
|
|
33
14
|
class LogtoError extends Error {
|
|
34
15
|
constructor(code, data) {
|
|
35
|
-
super(
|
|
16
|
+
super(logtoErrorCodes[code]);
|
|
36
17
|
this.code = code;
|
|
37
18
|
this.data = data;
|
|
38
19
|
}
|
|
39
20
|
}
|
|
40
21
|
const isLogtoRequestError = (data) => {
|
|
41
|
-
if (!
|
|
22
|
+
if (!isArbitraryObject(data)) {
|
|
42
23
|
return false;
|
|
43
24
|
}
|
|
44
25
|
return typeof data.code === 'string' && typeof data.message === 'string';
|
|
@@ -56,7 +37,4 @@ class OidcError {
|
|
|
56
37
|
}
|
|
57
38
|
}
|
|
58
39
|
|
|
59
|
-
|
|
60
|
-
exports.LogtoRequestError = LogtoRequestError;
|
|
61
|
-
exports.OidcError = OidcError;
|
|
62
|
-
exports.isLogtoRequestError = isLogtoRequestError;
|
|
40
|
+
export { LogtoError, LogtoRequestError, OidcError, isLogtoRequestError };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var essentials = require('@silverhand/essentials');
|
|
4
|
+
var jose = require('jose');
|
|
5
|
+
var arbitraryObject = require('./arbitrary-object.cjs');
|
|
6
|
+
var errors = require('./errors.cjs');
|
|
5
7
|
|
|
6
8
|
const issuedAtTimeTolerance = 60;
|
|
7
9
|
/* eslint-disable complexity */
|
|
@@ -9,7 +11,7 @@ const issuedAtTimeTolerance = 60;
|
|
|
9
11
|
* @link [ID Token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)
|
|
10
12
|
*/
|
|
11
13
|
function assertIdTokenClaims(data) {
|
|
12
|
-
if (!isArbitraryObject(data)) {
|
|
14
|
+
if (!arbitraryObject.isArbitraryObject(data)) {
|
|
13
15
|
throw new TypeError('IdToken is expected to be an object');
|
|
14
16
|
}
|
|
15
17
|
for (const key of ['iss', 'sub', 'aud']) {
|
|
@@ -41,20 +43,21 @@ function assertIdTokenClaims(data) {
|
|
|
41
43
|
}
|
|
42
44
|
/* eslint-enable complexity */
|
|
43
45
|
const verifyIdToken = async (idToken, clientId, issuer, jwks) => {
|
|
44
|
-
const result = await jwtVerify(idToken, jwks, { audience: clientId, issuer });
|
|
46
|
+
const result = await jose.jwtVerify(idToken, jwks, { audience: clientId, issuer });
|
|
45
47
|
if (Math.abs((result.payload.iat ?? 0) - Date.now() / 1000) > issuedAtTimeTolerance) {
|
|
46
|
-
throw new LogtoError('id_token.invalid_iat');
|
|
48
|
+
throw new errors.LogtoError('id_token.invalid_iat');
|
|
47
49
|
}
|
|
48
50
|
};
|
|
49
51
|
const decodeIdToken = (token) => {
|
|
50
52
|
const { 1: encodedPayload } = token.split('.');
|
|
51
53
|
if (!encodedPayload) {
|
|
52
|
-
throw new LogtoError('id_token.invalid_token');
|
|
54
|
+
throw new errors.LogtoError('id_token.invalid_token');
|
|
53
55
|
}
|
|
54
|
-
const json = urlSafeBase64.decode(encodedPayload);
|
|
56
|
+
const json = essentials.urlSafeBase64.decode(encodedPayload);
|
|
55
57
|
const idTokenClaims = JSON.parse(json);
|
|
56
58
|
assertIdTokenClaims(idTokenClaims);
|
|
57
59
|
return idTokenClaims;
|
|
58
60
|
};
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
exports.decodeIdToken = decodeIdToken;
|
|
63
|
+
exports.verifyIdToken = verifyIdToken;
|
package/lib/utils/id-token.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var arbitraryObject = require('./arbitrary-object.js');
|
|
6
|
-
var errors = require('./errors.js');
|
|
1
|
+
import { urlSafeBase64 } from '@silverhand/essentials';
|
|
2
|
+
import { jwtVerify } from 'jose';
|
|
3
|
+
import { isArbitraryObject } from './arbitrary-object.js';
|
|
4
|
+
import { LogtoError } from './errors.js';
|
|
7
5
|
|
|
8
6
|
const issuedAtTimeTolerance = 60;
|
|
9
7
|
/* eslint-disable complexity */
|
|
@@ -11,7 +9,7 @@ const issuedAtTimeTolerance = 60;
|
|
|
11
9
|
* @link [ID Token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)
|
|
12
10
|
*/
|
|
13
11
|
function assertIdTokenClaims(data) {
|
|
14
|
-
if (!
|
|
12
|
+
if (!isArbitraryObject(data)) {
|
|
15
13
|
throw new TypeError('IdToken is expected to be an object');
|
|
16
14
|
}
|
|
17
15
|
for (const key of ['iss', 'sub', 'aud']) {
|
|
@@ -43,21 +41,20 @@ function assertIdTokenClaims(data) {
|
|
|
43
41
|
}
|
|
44
42
|
/* eslint-enable complexity */
|
|
45
43
|
const verifyIdToken = async (idToken, clientId, issuer, jwks) => {
|
|
46
|
-
const result = await
|
|
44
|
+
const result = await jwtVerify(idToken, jwks, { audience: clientId, issuer });
|
|
47
45
|
if (Math.abs((result.payload.iat ?? 0) - Date.now() / 1000) > issuedAtTimeTolerance) {
|
|
48
|
-
throw new
|
|
46
|
+
throw new LogtoError('id_token.invalid_iat');
|
|
49
47
|
}
|
|
50
48
|
};
|
|
51
49
|
const decodeIdToken = (token) => {
|
|
52
50
|
const { 1: encodedPayload } = token.split('.');
|
|
53
51
|
if (!encodedPayload) {
|
|
54
|
-
throw new
|
|
52
|
+
throw new LogtoError('id_token.invalid_token');
|
|
55
53
|
}
|
|
56
|
-
const json =
|
|
54
|
+
const json = urlSafeBase64.decode(encodedPayload);
|
|
57
55
|
const idTokenClaims = JSON.parse(json);
|
|
58
56
|
assertIdTokenClaims(idTokenClaims);
|
|
59
57
|
return idTokenClaims;
|
|
60
58
|
};
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
exports.verifyIdToken = verifyIdToken;
|
|
60
|
+
export { decodeIdToken, verifyIdToken };
|
package/lib/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from './callback-uri';
|
|
2
|
-
export * from './errors';
|
|
3
|
-
export * from './id-token';
|
|
4
|
-
export * from './scopes';
|
|
5
|
-
export * from './arbitrary-object';
|
|
1
|
+
export * from './callback-uri.js';
|
|
2
|
+
export * from './errors.js';
|
|
3
|
+
export * from './id-token.js';
|
|
4
|
+
export * from './scopes.js';
|
|
5
|
+
export * from './arbitrary-object.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var index = require('../consts/index.cjs');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param originalScopes
|
|
7
|
+
* @return scopes should contain all default scopes (`openid`, `offline_access` and `profile`)
|
|
8
|
+
*/
|
|
9
|
+
const withDefaultScopes = (originalScopes) => {
|
|
10
|
+
const reservedScopes = Object.values(index.ReservedScope);
|
|
11
|
+
const uniqueScopes = new Set([...reservedScopes, index.UserScope.Profile, ...(originalScopes ?? [])]);
|
|
12
|
+
return Array.from(uniqueScopes).join(' ');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
exports.withDefaultScopes = withDefaultScopes;
|
package/lib/utils/scopes.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var index = require('../consts/index.js');
|
|
1
|
+
import { ReservedScope, UserScope } from '../consts/index.js';
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param originalScopes
|
|
7
5
|
* @return scopes should contain all default scopes (`openid`, `offline_access` and `profile`)
|
|
8
6
|
*/
|
|
9
7
|
const withDefaultScopes = (originalScopes) => {
|
|
10
|
-
const reservedScopes = Object.values(
|
|
11
|
-
const uniqueScopes = new Set([...reservedScopes,
|
|
8
|
+
const reservedScopes = Object.values(ReservedScope);
|
|
9
|
+
const uniqueScopes = new Set([...reservedScopes, UserScope.Profile, ...(originalScopes ?? [])]);
|
|
12
10
|
return Array.from(uniqueScopes).join(' ');
|
|
13
11
|
};
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
export { withDefaultScopes };
|
package/package.json
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/js",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"main": "./lib/index.
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./lib/index.cjs",
|
|
6
|
+
"module": "./lib/index.js",
|
|
7
|
+
"types": "./lib/index.d.ts",
|
|
6
8
|
"exports": {
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
+
"types": "./lib/index.d.ts",
|
|
10
|
+
"require": "./lib/index.cjs",
|
|
11
|
+
"import": "./lib/index.js"
|
|
9
12
|
},
|
|
10
|
-
"module": "./lib/index.mjs",
|
|
11
|
-
"types": "./lib/index.d.ts",
|
|
12
13
|
"files": [
|
|
13
14
|
"lib"
|
|
14
15
|
],
|
|
@@ -18,30 +19,17 @@
|
|
|
18
19
|
"url": "https://github.com/logto-io/js.git",
|
|
19
20
|
"directory": "packages/js"
|
|
20
21
|
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"dev:tsc": "tsc -p tsconfig.build.json -w --preserveWatchOutput",
|
|
23
|
-
"precommit": "lint-staged",
|
|
24
|
-
"check": "tsc --noEmit",
|
|
25
|
-
"build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c",
|
|
26
|
-
"lint": "eslint --ext .ts src",
|
|
27
|
-
"test": "jest",
|
|
28
|
-
"test:coverage": "jest --silent --env=jsdom && jest --silent",
|
|
29
|
-
"prepack": "pnpm test"
|
|
30
|
-
},
|
|
31
22
|
"dependencies": {
|
|
32
|
-
"@silverhand/essentials": "^2.6.
|
|
23
|
+
"@silverhand/essentials": "^2.6.2",
|
|
33
24
|
"camelcase-keys": "^7.0.1",
|
|
34
|
-
"jose": "^4.13.2"
|
|
35
|
-
"lodash.get": "^4.4.2"
|
|
25
|
+
"jose": "^4.13.2"
|
|
36
26
|
},
|
|
37
27
|
"devDependencies": {
|
|
38
|
-
"@
|
|
39
|
-
"@silverhand/
|
|
40
|
-
"@silverhand/ts-config": "^1.0.0",
|
|
28
|
+
"@silverhand/eslint-config": "^3.0.1",
|
|
29
|
+
"@silverhand/ts-config": "^3.0.0",
|
|
41
30
|
"@swc/core": "^1.3.50",
|
|
42
31
|
"@swc/jest": "^0.2.24",
|
|
43
|
-
"@types/jest": "^29.5.
|
|
44
|
-
"@types/lodash.get": "^4.4.6",
|
|
32
|
+
"@types/jest": "^29.5.1",
|
|
45
33
|
"@types/node": "^18.0.0",
|
|
46
34
|
"eslint": "^8.38.0",
|
|
47
35
|
"jest": "^29.5.0",
|
|
@@ -62,5 +50,13 @@
|
|
|
62
50
|
"publishConfig": {
|
|
63
51
|
"access": "public"
|
|
64
52
|
},
|
|
65
|
-
"
|
|
66
|
-
|
|
53
|
+
"scripts": {
|
|
54
|
+
"dev:tsc": "tsc -p tsconfig.build.json -w --preserveWatchOutput",
|
|
55
|
+
"precommit": "lint-staged",
|
|
56
|
+
"check": "tsc --noEmit",
|
|
57
|
+
"build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c",
|
|
58
|
+
"lint": "eslint --ext .ts src",
|
|
59
|
+
"test": "jest",
|
|
60
|
+
"test:coverage": "jest --silent --env=jsdom && jest --silent"
|
|
61
|
+
}
|
|
62
|
+
}
|
package/lib/core/fetch-token.mjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import camelcaseKeys from 'camelcase-keys';
|
|
2
|
-
import { QueryKey, TokenGrantType, ContentType } from '../consts/index.mjs';
|
|
3
|
-
|
|
4
|
-
const fetchTokenByAuthorizationCode = async ({ clientId, tokenEndpoint, redirectUri, codeVerifier, code, resource, }, requester) => {
|
|
5
|
-
const parameters = new URLSearchParams();
|
|
6
|
-
parameters.append(QueryKey.ClientId, clientId);
|
|
7
|
-
parameters.append(QueryKey.Code, code);
|
|
8
|
-
parameters.append(QueryKey.CodeVerifier, codeVerifier);
|
|
9
|
-
parameters.append(QueryKey.RedirectUri, redirectUri);
|
|
10
|
-
parameters.append(QueryKey.GrantType, TokenGrantType.AuthorizationCode);
|
|
11
|
-
if (resource) {
|
|
12
|
-
parameters.append(QueryKey.Resource, resource);
|
|
13
|
-
}
|
|
14
|
-
const snakeCaseCodeTokenResponse = await requester(tokenEndpoint, {
|
|
15
|
-
method: 'POST',
|
|
16
|
-
headers: ContentType.formUrlEncoded,
|
|
17
|
-
body: parameters,
|
|
18
|
-
});
|
|
19
|
-
return camelcaseKeys(snakeCaseCodeTokenResponse);
|
|
20
|
-
};
|
|
21
|
-
const fetchTokenByRefreshToken = async ({ clientId, tokenEndpoint, refreshToken, resource, scopes }, requester) => {
|
|
22
|
-
const parameters = new URLSearchParams();
|
|
23
|
-
parameters.append(QueryKey.ClientId, clientId);
|
|
24
|
-
parameters.append(QueryKey.RefreshToken, refreshToken);
|
|
25
|
-
parameters.append(QueryKey.GrantType, TokenGrantType.RefreshToken);
|
|
26
|
-
if (resource) {
|
|
27
|
-
parameters.append(QueryKey.Resource, resource);
|
|
28
|
-
}
|
|
29
|
-
if (scopes?.length) {
|
|
30
|
-
parameters.append(QueryKey.Scope, scopes.join(' '));
|
|
31
|
-
}
|
|
32
|
-
const snakeCaseRefreshTokenTokenResponse = await requester(tokenEndpoint, {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
headers: ContentType.formUrlEncoded,
|
|
35
|
-
body: parameters,
|
|
36
|
-
});
|
|
37
|
-
return camelcaseKeys(snakeCaseRefreshTokenTokenResponse);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export { fetchTokenByAuthorizationCode, fetchTokenByRefreshToken };
|
package/lib/core/oidc-config.mjs
DELETED
package/lib/core/revoke.mjs
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { ContentType, QueryKey } from '../consts/index.mjs';
|
|
2
|
-
|
|
3
|
-
const revoke = async (revocationEndpoint, clientId, token, requester) => requester(revocationEndpoint, {
|
|
4
|
-
method: 'POST',
|
|
5
|
-
headers: ContentType.formUrlEncoded,
|
|
6
|
-
body: new URLSearchParams({
|
|
7
|
-
[QueryKey.ClientId]: clientId,
|
|
8
|
-
[QueryKey.Token]: token,
|
|
9
|
-
}),
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export { revoke };
|
package/lib/core/sign-in.mjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { QueryKey, Prompt } from '../consts/index.mjs';
|
|
2
|
-
import '@silverhand/essentials';
|
|
3
|
-
import 'lodash.get';
|
|
4
|
-
import 'jose';
|
|
5
|
-
import { withDefaultScopes } from '../utils/scopes.mjs';
|
|
6
|
-
|
|
7
|
-
const codeChallengeMethod = 'S256';
|
|
8
|
-
const responseType = 'code';
|
|
9
|
-
const generateSignInUri = ({ authorizationEndpoint, clientId, redirectUri, codeChallenge, state, scopes, resources, prompt, interactionMode, }) => {
|
|
10
|
-
const urlSearchParameters = new URLSearchParams({
|
|
11
|
-
[QueryKey.ClientId]: clientId,
|
|
12
|
-
[QueryKey.RedirectUri]: redirectUri,
|
|
13
|
-
[QueryKey.CodeChallenge]: codeChallenge,
|
|
14
|
-
[QueryKey.CodeChallengeMethod]: codeChallengeMethod,
|
|
15
|
-
[QueryKey.State]: state,
|
|
16
|
-
[QueryKey.ResponseType]: responseType,
|
|
17
|
-
[QueryKey.Prompt]: prompt ?? Prompt.Consent,
|
|
18
|
-
[QueryKey.Scope]: withDefaultScopes(scopes),
|
|
19
|
-
});
|
|
20
|
-
for (const resource of resources ?? []) {
|
|
21
|
-
urlSearchParameters.append(QueryKey.Resource, resource);
|
|
22
|
-
}
|
|
23
|
-
// Set interactionMode to signUp for a create account user experience
|
|
24
|
-
if (interactionMode) {
|
|
25
|
-
urlSearchParameters.append(QueryKey.InteractionMode, interactionMode);
|
|
26
|
-
}
|
|
27
|
-
return `${authorizationEndpoint}?${urlSearchParameters.toString()}`;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export { generateSignInUri };
|
package/lib/core/sign-out.mjs
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { QueryKey } from '../consts/index.mjs';
|
|
2
|
-
|
|
3
|
-
const generateSignOutUri = ({ endSessionEndpoint, clientId, postLogoutRedirectUri, }) => {
|
|
4
|
-
const urlSearchParameters = new URLSearchParams({ [QueryKey.ClientId]: clientId });
|
|
5
|
-
if (postLogoutRedirectUri) {
|
|
6
|
-
urlSearchParameters.append(QueryKey.PostLogoutRedirectUri, postLogoutRedirectUri);
|
|
7
|
-
}
|
|
8
|
-
return `${endSessionEndpoint}?${urlSearchParameters.toString()}`;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export { generateSignOutUri };
|
package/lib/index.mjs
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export { fetchTokenByAuthorizationCode, fetchTokenByRefreshToken } from './core/fetch-token.mjs';
|
|
2
|
-
export { discoveryPath, fetchOidcConfig } from './core/oidc-config.mjs';
|
|
3
|
-
export { revoke } from './core/revoke.mjs';
|
|
4
|
-
export { generateSignInUri } from './core/sign-in.mjs';
|
|
5
|
-
export { generateSignOutUri } from './core/sign-out.mjs';
|
|
6
|
-
export { fetchUserInfo } from './core/user-info.mjs';
|
|
7
|
-
export { parseUriParameters, verifyAndParseCodeFromCallbackUri } from './utils/callback-uri.mjs';
|
|
8
|
-
export { LogtoError, LogtoRequestError, OidcError, isLogtoRequestError } from './utils/errors.mjs';
|
|
9
|
-
export { decodeIdToken, verifyIdToken } from './utils/id-token.mjs';
|
|
10
|
-
export { withDefaultScopes } from './utils/scopes.mjs';
|
|
11
|
-
export { isArbitraryObject } from './utils/arbitrary-object.mjs';
|
|
12
|
-
export { ContentType, Prompt, QueryKey, ReservedScope, TokenGrantType, UserScope } from './consts/index.mjs';
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { conditional } from '@silverhand/essentials';
|
|
2
|
-
import { QueryKey } from '../consts/index.mjs';
|
|
3
|
-
import { LogtoError, OidcError } from './errors.mjs';
|
|
4
|
-
|
|
5
|
-
const parseUriParameters = (uri) => {
|
|
6
|
-
const [, queryString = ''] = uri.split('?');
|
|
7
|
-
return new URLSearchParams(queryString);
|
|
8
|
-
};
|
|
9
|
-
const verifyAndParseCodeFromCallbackUri = (callbackUri, redirectUri, state) => {
|
|
10
|
-
if (!callbackUri.startsWith(redirectUri)) {
|
|
11
|
-
throw new LogtoError('callback_uri_verification.redirect_uri_mismatched');
|
|
12
|
-
}
|
|
13
|
-
const uriParameters = parseUriParameters(callbackUri);
|
|
14
|
-
const error = conditional(uriParameters.get(QueryKey.Error));
|
|
15
|
-
const errorDescription = conditional(uriParameters.get(QueryKey.ErrorDescription));
|
|
16
|
-
if (error) {
|
|
17
|
-
throw new LogtoError('callback_uri_verification.error_found', new OidcError(error, errorDescription));
|
|
18
|
-
}
|
|
19
|
-
const stateFromCallbackUri = uriParameters.get(QueryKey.State);
|
|
20
|
-
if (!stateFromCallbackUri) {
|
|
21
|
-
throw new LogtoError('callback_uri_verification.missing_state');
|
|
22
|
-
}
|
|
23
|
-
if (stateFromCallbackUri !== state) {
|
|
24
|
-
throw new LogtoError('callback_uri_verification.state_mismatched');
|
|
25
|
-
}
|
|
26
|
-
const code = uriParameters.get(QueryKey.Code);
|
|
27
|
-
if (!code) {
|
|
28
|
-
throw new LogtoError('callback_uri_verification.missing_code');
|
|
29
|
-
}
|
|
30
|
-
return code;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export { parseUriParameters, verifyAndParseCodeFromCallbackUri };
|