@pvh-afl/core 1.1.21 → 1.1.23
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/dist/auth/auth.module.d.ts +58 -0
- package/dist/auth/auth.module.d.ts.map +1 -0
- package/dist/auth/auth.module.js +87 -0
- package/dist/auth/auth.module.js.map +1 -0
- package/dist/auth/auth.types.d.ts +122 -0
- package/dist/auth/auth.types.d.ts.map +1 -0
- package/dist/auth/auth.types.js +41 -0
- package/dist/auth/auth.types.js.map +1 -0
- package/dist/auth/current-user.decorator.d.ts +17 -0
- package/dist/auth/current-user.decorator.d.ts.map +1 -0
- package/dist/auth/current-user.decorator.js +22 -0
- package/dist/auth/current-user.decorator.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +19 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jwt-auth.guard.d.ts +21 -0
- package/dist/auth/jwt-auth.guard.d.ts.map +1 -0
- package/dist/auth/jwt-auth.guard.js +84 -0
- package/dist/auth/jwt-auth.guard.js.map +1 -0
- package/dist/auth/jwt.service.d.ts +93 -0
- package/dist/auth/jwt.service.d.ts.map +1 -0
- package/dist/auth/jwt.service.js +185 -0
- package/dist/auth/jwt.service.js.map +1 -0
- package/dist/auth/optional-jwt-auth.guard.d.ts +34 -0
- package/dist/auth/optional-jwt-auth.guard.d.ts.map +1 -0
- package/dist/auth/optional-jwt-auth.guard.js +55 -0
- package/dist/auth/optional-jwt-auth.guard.js.map +1 -0
- package/dist/auth/ownership.d.ts +65 -0
- package/dist/auth/ownership.d.ts.map +1 -0
- package/dist/auth/ownership.js +117 -0
- package/dist/auth/ownership.js.map +1 -0
- package/dist/auth/utils.d.ts +53 -0
- package/dist/auth/utils.d.ts.map +1 -0
- package/dist/auth/utils.js +86 -0
- package/dist/auth/utils.js.map +1 -0
- package/dist/integrations/commerce/commerce.types.d.ts +37 -1
- package/dist/integrations/commerce/commerce.types.d.ts.map +1 -1
- package/dist/integrations/commerce/shopify.service.d.ts +2 -2
- package/dist/integrations/commerce/shopify.service.d.ts.map +1 -1
- package/dist/integrations/commerce/shopify.service.js +53 -8
- package/dist/integrations/commerce/shopify.service.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { DynamicModule, InjectionToken, OptionalFactoryDependency, Type } from '@nestjs/common';
|
|
2
|
+
import { AuthModuleOptions } from './auth.types';
|
|
3
|
+
/**
|
|
4
|
+
* Authentication module.
|
|
5
|
+
*
|
|
6
|
+
* Provides {@link AuthTokenService} for issuing tokens and {@link JwtAuthGuard}
|
|
7
|
+
* for requiring one. Registered global so a guard applied in any module can
|
|
8
|
+
* resolve its dependencies without that module importing this one.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* AuthModule.forRoot({
|
|
13
|
+
* privateKey: process.env.AUTH_JWT_PRIVATE_KEY!,
|
|
14
|
+
* publicKey: process.env.AUTH_JWT_PUBLIC_KEY!,
|
|
15
|
+
* issuer: process.env.AUTH_JWT_ISSUER!,
|
|
16
|
+
* audience: process.env.AUTH_JWT_AUDIENCE!,
|
|
17
|
+
* ttlSeconds: Number(process.env.AUTH_JWT_TTL ?? 1800),
|
|
18
|
+
* })
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Options for {@link AuthModule.forRootAsync}.
|
|
23
|
+
*
|
|
24
|
+
* Use this when the settings come from a provider — typically `ConfigService`,
|
|
25
|
+
* so authentication is configured from the brand config file alongside every
|
|
26
|
+
* other integration rather than read from `process.env` at module definition.
|
|
27
|
+
*/
|
|
28
|
+
export interface AuthModuleAsyncOptions {
|
|
29
|
+
imports?: Type<unknown>[];
|
|
30
|
+
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
31
|
+
/**
|
|
32
|
+
* `any[]` rather than `unknown[]`: parameters are contravariant under
|
|
33
|
+
* `strictFunctionTypes`, so a factory declared as `(config: ConfigService)`
|
|
34
|
+
* would not be assignable to one declared as `(...args: unknown[])`. This
|
|
35
|
+
* mirrors how Nest types its own async module options.
|
|
36
|
+
*/
|
|
37
|
+
useFactory: (...args: any[]) => AuthModuleOptions | Promise<AuthModuleOptions>;
|
|
38
|
+
}
|
|
39
|
+
export declare class AuthModule {
|
|
40
|
+
/**
|
|
41
|
+
* Configure from values resolved by a provider.
|
|
42
|
+
*
|
|
43
|
+
* The factory runs during application init, so a missing key still fails at
|
|
44
|
+
* startup rather than at the first request.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* AuthModule.forRootAsync({
|
|
49
|
+
* imports: [ConfigModule],
|
|
50
|
+
* inject: [ConfigService],
|
|
51
|
+
* useFactory: (config: ConfigService) => config.getAuthConfig('tommy'),
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
static forRootAsync(options: AuthModuleAsyncOptions): DynamicModule;
|
|
56
|
+
static forRoot(options: AuthModuleOptions): DynamicModule;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=auth.module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,cAAc,EAEd,yBAAyB,EAEzB,IAAI,EACL,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAgB,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAK/D;;;;;;;;;;;;;;;;;GAiBG;AACH;;;;;;GAMG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,KAAK,CAAC,cAAc,GAAG,yBAAyB,CAAC,CAAC;IAC3D;;;;;OAKG;IAEH,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAChF;AAED,qBACa,UAAU;IACrB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,sBAAsB,GAAG,aAAa;IAqBnE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,iBAAiB,GAAG,aAAa;CAmB1D"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var AuthModule_1;
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AuthModule = void 0;
|
|
5
|
+
const tslib_1 = require("tslib");
|
|
6
|
+
const common_1 = require("@nestjs/common");
|
|
7
|
+
const jwt_1 = require("@nestjs/jwt");
|
|
8
|
+
const auth_types_1 = require("./auth.types");
|
|
9
|
+
const jwt_service_1 = require("./jwt.service");
|
|
10
|
+
const jwt_auth_guard_1 = require("./jwt-auth.guard");
|
|
11
|
+
const optional_jwt_auth_guard_1 = require("./optional-jwt-auth.guard");
|
|
12
|
+
let AuthModule = AuthModule_1 = class AuthModule {
|
|
13
|
+
/**
|
|
14
|
+
* Configure from values resolved by a provider.
|
|
15
|
+
*
|
|
16
|
+
* The factory runs during application init, so a missing key still fails at
|
|
17
|
+
* startup rather than at the first request.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* AuthModule.forRootAsync({
|
|
22
|
+
* imports: [ConfigModule],
|
|
23
|
+
* inject: [ConfigService],
|
|
24
|
+
* useFactory: (config: ConfigService) => config.getAuthConfig('tommy'),
|
|
25
|
+
* })
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
static forRootAsync(options) {
|
|
29
|
+
const optionsProvider = {
|
|
30
|
+
provide: auth_types_1.AUTH_OPTIONS,
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
|
+
useFactory: async (...args) => {
|
|
33
|
+
const resolved = await options.useFactory(...args);
|
|
34
|
+
assertUsableOptions(resolved);
|
|
35
|
+
return resolved;
|
|
36
|
+
},
|
|
37
|
+
inject: options.inject ?? [],
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
module: AuthModule_1,
|
|
41
|
+
global: true,
|
|
42
|
+
imports: [jwt_1.JwtModule.register({}), ...(options.imports ?? [])],
|
|
43
|
+
providers: [optionsProvider, jwt_service_1.AuthTokenService, jwt_auth_guard_1.JwtAuthGuard, optional_jwt_auth_guard_1.OptionalJwtAuthGuard],
|
|
44
|
+
exports: [jwt_service_1.AuthTokenService, jwt_auth_guard_1.JwtAuthGuard, optional_jwt_auth_guard_1.OptionalJwtAuthGuard],
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
static forRoot(options) {
|
|
48
|
+
assertUsableOptions(options);
|
|
49
|
+
return {
|
|
50
|
+
module: AuthModule_1,
|
|
51
|
+
global: true,
|
|
52
|
+
// Keys are passed per call in AuthTokenService rather than registered
|
|
53
|
+
// here, so the pinned algorithm and the key travel together and cannot
|
|
54
|
+
// drift apart.
|
|
55
|
+
imports: [jwt_1.JwtModule.register({})],
|
|
56
|
+
providers: [
|
|
57
|
+
{ provide: auth_types_1.AUTH_OPTIONS, useValue: options },
|
|
58
|
+
jwt_service_1.AuthTokenService,
|
|
59
|
+
jwt_auth_guard_1.JwtAuthGuard,
|
|
60
|
+
optional_jwt_auth_guard_1.OptionalJwtAuthGuard,
|
|
61
|
+
],
|
|
62
|
+
exports: [jwt_service_1.AuthTokenService, jwt_auth_guard_1.JwtAuthGuard, optional_jwt_auth_guard_1.OptionalJwtAuthGuard],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
exports.AuthModule = AuthModule;
|
|
67
|
+
exports.AuthModule = AuthModule = AuthModule_1 = tslib_1.__decorate([
|
|
68
|
+
(0, common_1.Module)({})
|
|
69
|
+
], AuthModule);
|
|
70
|
+
/**
|
|
71
|
+
* Fail at startup rather than at the first request.
|
|
72
|
+
*
|
|
73
|
+
* A missing key would otherwise surface as every token failing to verify, which
|
|
74
|
+
* looks like a client bug and is slow to trace back to configuration. An empty
|
|
75
|
+
* environment variable is the likely cause, so it is worth catching loudly.
|
|
76
|
+
*/
|
|
77
|
+
function assertUsableOptions(options) {
|
|
78
|
+
const missing = ['privateKey', 'publicKey', 'issuer', 'audience'].filter((key) => {
|
|
79
|
+
const value = options?.[key];
|
|
80
|
+
return typeof value !== 'string' || value.trim() === '';
|
|
81
|
+
});
|
|
82
|
+
if (missing.length > 0) {
|
|
83
|
+
throw new Error(`AuthModule.forRoot() is missing required options: ${missing.join(', ')}. ` +
|
|
84
|
+
'Check the AUTH_JWT_* environment variables.');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=auth.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":";;;;;AAAA,2CAOwB;AACxB,qCAAwC;AACxC,6CAA+D;AAC/D,+CAAiD;AACjD,qDAAgD;AAChD,uEAAiE;AAyC1D,IAAM,UAAU,kBAAhB,MAAM,UAAU;IACrB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,YAAY,CAAC,OAA+B;QACjD,MAAM,eAAe,GAAa;YAChC,OAAO,EAAE,yBAAY;YACrB,8DAA8D;YAC9D,UAAU,EAAE,KAAK,EAAE,GAAG,IAAW,EAAE,EAAE;gBACnC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,CAAC;gBACnD,mBAAmB,CAAC,QAAQ,CAAC,CAAC;gBAC9B,OAAO,QAAQ,CAAC;YAClB,CAAC;YACD,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;SAC7B,CAAC;QAEF,OAAO;YACL,MAAM,EAAE,YAAU;YAClB,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,CAAC,eAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAC7D,SAAS,EAAE,CAAC,eAAe,EAAE,8BAAgB,EAAE,6BAAY,EAAE,8CAAoB,CAAC;YAClF,OAAO,EAAE,CAAC,8BAAgB,EAAE,6BAAY,EAAE,8CAAoB,CAAC;SAChE,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,OAA0B;QACvC,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAE7B,OAAO;YACL,MAAM,EAAE,YAAU;YAClB,MAAM,EAAE,IAAI;YACZ,sEAAsE;YACtE,uEAAuE;YACvE,eAAe;YACf,OAAO,EAAE,CAAC,eAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YACjC,SAAS,EAAE;gBACT,EAAE,OAAO,EAAE,yBAAY,EAAE,QAAQ,EAAE,OAAO,EAAE;gBAC5C,8BAAgB;gBAChB,6BAAY;gBACZ,8CAAoB;aACrB;YACD,OAAO,EAAE,CAAC,8BAAgB,EAAE,6BAAY,EAAE,8CAAoB,CAAC;SAChE,CAAC;IACJ,CAAC;CACF,CAAA;AAxDY,gCAAU;qBAAV,UAAU;IADtB,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,UAAU,CAwDtB;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,OAA0B;IACrD,MAAM,OAAO,GACX,CAAC,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,CACjD,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;QACf,MAAM,KAAK,GAAG,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,qDAAqD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YACzE,6CAA6C,CAChD,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication types.
|
|
3
|
+
*
|
|
4
|
+
* The JWT issued here is an authentication gate only. It proves *which customer*
|
|
5
|
+
* is calling; it carries no Shopify credential and grants no capability of its
|
|
6
|
+
* own. Shopify remains the source of truth for identity — the token is minted
|
|
7
|
+
* only after Shopify has validated a customer access token.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Configuration for {@link AuthModule.forRoot}.
|
|
11
|
+
*/
|
|
12
|
+
export interface AuthModuleOptions {
|
|
13
|
+
/** RSA private key (PEM) used to sign tokens. Keep in a secret manager. */
|
|
14
|
+
privateKey: string;
|
|
15
|
+
/** RSA public key (PEM) used to verify tokens. Safe to distribute. */
|
|
16
|
+
publicKey: string;
|
|
17
|
+
/** `iss` claim, and the only issuer accepted on verify. */
|
|
18
|
+
issuer: string;
|
|
19
|
+
/** `aud` claim, and the only audience accepted on verify. */
|
|
20
|
+
audience: string;
|
|
21
|
+
/**
|
|
22
|
+
* Access token lifetime in seconds. Kept short because there is no
|
|
23
|
+
* server-side revocation — a stolen token is usable until it expires.
|
|
24
|
+
*
|
|
25
|
+
* @default 1800 (30 minutes)
|
|
26
|
+
*/
|
|
27
|
+
ttlSeconds?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Refresh token lifetime in seconds.
|
|
30
|
+
*
|
|
31
|
+
* This is the absolute length of a session: refresh tokens are **not** rotated,
|
|
32
|
+
* so the customer logs in again when this elapses regardless of activity.
|
|
33
|
+
*
|
|
34
|
+
* Not rotating is deliberate. Rotation only adds safety when the server can
|
|
35
|
+
* remember which token is current and spot an old one being replayed, and that
|
|
36
|
+
* needs stored state. Without it, rotation would instead let a stolen refresh
|
|
37
|
+
* token be renewed indefinitely. A fixed expiry means a stolen token dies at a
|
|
38
|
+
* known time.
|
|
39
|
+
*
|
|
40
|
+
* Raising this widens the window in which a stolen refresh token works, and
|
|
41
|
+
* there is no way to cut that short.
|
|
42
|
+
*
|
|
43
|
+
* @default 604800 (7 days)
|
|
44
|
+
*/
|
|
45
|
+
refreshTtlSeconds?: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Which kind of token this is.
|
|
49
|
+
*
|
|
50
|
+
* Carried in every token and checked on every verify, so the two cannot be
|
|
51
|
+
* swapped. Without it a refresh token — deliberately long-lived — would be
|
|
52
|
+
* accepted as an access token and quietly bypass the short access window.
|
|
53
|
+
*/
|
|
54
|
+
export declare const TokenType: {
|
|
55
|
+
readonly ACCESS: "access";
|
|
56
|
+
readonly REFRESH: "refresh";
|
|
57
|
+
};
|
|
58
|
+
export type TokenType = (typeof TokenType)[keyof typeof TokenType];
|
|
59
|
+
/**
|
|
60
|
+
* The claims carried in a token we issue.
|
|
61
|
+
*
|
|
62
|
+
* Everything here is readable by anyone holding the token — base64url is
|
|
63
|
+
* encoding, not encryption. Nothing secret goes in, which is why the phone
|
|
64
|
+
* number is stored as a hash rather than in the clear.
|
|
65
|
+
*/
|
|
66
|
+
export interface CustomerJwtPayload {
|
|
67
|
+
/** Issuer. */
|
|
68
|
+
iss: string;
|
|
69
|
+
/** Audience. */
|
|
70
|
+
aud: string;
|
|
71
|
+
/** Access or refresh. Enforced on verify so the two cannot be interchanged. */
|
|
72
|
+
typ: TokenType;
|
|
73
|
+
/**
|
|
74
|
+
* The authenticated customer, as a Shopify GID.
|
|
75
|
+
* Read from Shopify's response at issue time — never from client input.
|
|
76
|
+
*/
|
|
77
|
+
sub: string;
|
|
78
|
+
/**
|
|
79
|
+
* SHA-256 of the customer's normalised mobile number, or undefined when
|
|
80
|
+
* Shopify holds no phone number for them.
|
|
81
|
+
*
|
|
82
|
+
* Enough to verify that a `mobile` value in a request belongs to the caller,
|
|
83
|
+
* without putting the number itself in a readable token.
|
|
84
|
+
*/
|
|
85
|
+
phone_hash?: string;
|
|
86
|
+
/** Issued at (seconds since epoch). */
|
|
87
|
+
iat: number;
|
|
88
|
+
/** Expires at (seconds since epoch). */
|
|
89
|
+
exp: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* What the guard attaches to the request after a token verifies.
|
|
93
|
+
*
|
|
94
|
+
* Handlers and resolvers must read identity from here — never from the request
|
|
95
|
+
* body, query string or GraphQL arguments.
|
|
96
|
+
*/
|
|
97
|
+
export interface AuthUser {
|
|
98
|
+
/** Shopify customer GID, from the token's `sub`. */
|
|
99
|
+
customerId: string;
|
|
100
|
+
/** SHA-256 of the caller's normalised mobile, when Shopify has one. */
|
|
101
|
+
phoneHash?: string;
|
|
102
|
+
}
|
|
103
|
+
/** Error codes returned to callers. Stable — clients branch on these. */
|
|
104
|
+
export declare const AuthErrorCode: {
|
|
105
|
+
/** No `Authorization` header, or not a Bearer token. */
|
|
106
|
+
readonly TOKEN_MISSING: "TOKEN_MISSING";
|
|
107
|
+
/** Signature, issuer, audience or shape rejected. */
|
|
108
|
+
readonly TOKEN_INVALID: "TOKEN_INVALID";
|
|
109
|
+
/** Well-formed and correctly signed, but past `exp`. Refresh and retry. */
|
|
110
|
+
readonly TOKEN_EXPIRED: "TOKEN_EXPIRED";
|
|
111
|
+
/** Authenticated, but the request names a resource belonging to someone else. */
|
|
112
|
+
readonly FORBIDDEN: "FORBIDDEN";
|
|
113
|
+
/**
|
|
114
|
+
* A refresh token was sent where an access token belongs, or the reverse.
|
|
115
|
+
* Signals a client bug rather than an expired session.
|
|
116
|
+
*/
|
|
117
|
+
readonly TOKEN_WRONG_TYPE: "TOKEN_WRONG_TYPE";
|
|
118
|
+
};
|
|
119
|
+
export type AuthErrorCode = (typeof AuthErrorCode)[keyof typeof AuthErrorCode];
|
|
120
|
+
/** DI token for the resolved {@link AuthModuleOptions}. */
|
|
121
|
+
export declare const AUTH_OPTIONS: unique symbol;
|
|
122
|
+
//# sourceMappingURL=auth.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.types.d.ts","sourceRoot":"","sources":["../../src/auth/auth.types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;IAEnB,sEAAsE;IACtE,SAAS,EAAE,MAAM,CAAC;IAElB,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC;IAEf,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IAEjB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,eAAO,MAAM,SAAS;;;CAGZ,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC;AAEnE;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,cAAc;IACd,GAAG,EAAE,MAAM,CAAC;IAEZ,gBAAgB;IAChB,GAAG,EAAE,MAAM,CAAC;IAEZ,+EAA+E;IAC/E,GAAG,EAAE,SAAS,CAAC;IAEf;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;IAEZ,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IAEnB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,eAAO,MAAM,aAAa;IACxB,wDAAwD;;IAGxD,qDAAqD;;IAGrD,2EAA2E;;IAG3E,iFAAiF;;IAGjF;;;OAGG;;CAEK,CAAC;AAEX,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,OAAO,aAAa,CAAC,CAAC;AAE/E,2DAA2D;AAC3D,eAAO,MAAM,YAAY,eAAyB,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Authentication types.
|
|
4
|
+
*
|
|
5
|
+
* The JWT issued here is an authentication gate only. It proves *which customer*
|
|
6
|
+
* is calling; it carries no Shopify credential and grants no capability of its
|
|
7
|
+
* own. Shopify remains the source of truth for identity — the token is minted
|
|
8
|
+
* only after Shopify has validated a customer access token.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.AUTH_OPTIONS = exports.AuthErrorCode = exports.TokenType = void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Which kind of token this is.
|
|
14
|
+
*
|
|
15
|
+
* Carried in every token and checked on every verify, so the two cannot be
|
|
16
|
+
* swapped. Without it a refresh token — deliberately long-lived — would be
|
|
17
|
+
* accepted as an access token and quietly bypass the short access window.
|
|
18
|
+
*/
|
|
19
|
+
exports.TokenType = {
|
|
20
|
+
ACCESS: 'access',
|
|
21
|
+
REFRESH: 'refresh',
|
|
22
|
+
};
|
|
23
|
+
/** Error codes returned to callers. Stable — clients branch on these. */
|
|
24
|
+
exports.AuthErrorCode = {
|
|
25
|
+
/** No `Authorization` header, or not a Bearer token. */
|
|
26
|
+
TOKEN_MISSING: 'TOKEN_MISSING',
|
|
27
|
+
/** Signature, issuer, audience or shape rejected. */
|
|
28
|
+
TOKEN_INVALID: 'TOKEN_INVALID',
|
|
29
|
+
/** Well-formed and correctly signed, but past `exp`. Refresh and retry. */
|
|
30
|
+
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
|
31
|
+
/** Authenticated, but the request names a resource belonging to someone else. */
|
|
32
|
+
FORBIDDEN: 'FORBIDDEN',
|
|
33
|
+
/**
|
|
34
|
+
* A refresh token was sent where an access token belongs, or the reverse.
|
|
35
|
+
* Signals a client bug rather than an expired session.
|
|
36
|
+
*/
|
|
37
|
+
TOKEN_WRONG_TYPE: 'TOKEN_WRONG_TYPE',
|
|
38
|
+
};
|
|
39
|
+
/** DI token for the resolved {@link AuthModuleOptions}. */
|
|
40
|
+
exports.AUTH_OPTIONS = Symbol('AUTH_OPTIONS');
|
|
41
|
+
//# sourceMappingURL=auth.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.types.js","sourceRoot":"","sources":["../../src/auth/auth.types.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AA8CH;;;;;;GAMG;AACU,QAAA,SAAS,GAAG;IACvB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,SAAS;CACV,CAAC;AAyDX,yEAAyE;AAC5D,QAAA,aAAa,GAAG;IAC3B,wDAAwD;IACxD,aAAa,EAAE,eAAe;IAE9B,qDAAqD;IACrD,aAAa,EAAE,eAAe;IAE9B,2EAA2E;IAC3E,aAAa,EAAE,eAAe;IAE9B,iFAAiF;IACjF,SAAS,EAAE,WAAW;IAEtB;;;OAGG;IACH,gBAAgB,EAAE,kBAAkB;CAC5B,CAAC;AAIX,2DAA2D;AAC9C,QAAA,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Injects the authenticated caller, as established by {@link JwtAuthGuard}.
|
|
3
|
+
*
|
|
4
|
+
* Only valid on handlers that carry the guard — without it there is no verified
|
|
5
|
+
* user and this resolves to undefined.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* @UseGuards(JwtAuthGuard)
|
|
10
|
+
* @Get()
|
|
11
|
+
* async getItems(@CurrentUser() user: AuthUser) {
|
|
12
|
+
* return this.service.getItems(user.customerId);
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export declare const CurrentUser: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
17
|
+
//# sourceMappingURL=current-user.decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"current-user.decorator.d.ts","sourceRoot":"","sources":["../../src/auth/current-user.decorator.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW,mDAGvB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CurrentUser = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
/**
|
|
7
|
+
* Injects the authenticated caller, as established by {@link JwtAuthGuard}.
|
|
8
|
+
*
|
|
9
|
+
* Only valid on handlers that carry the guard — without it there is no verified
|
|
10
|
+
* user and this resolves to undefined.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* @UseGuards(JwtAuthGuard)
|
|
15
|
+
* @Get()
|
|
16
|
+
* async getItems(@CurrentUser() user: AuthUser) {
|
|
17
|
+
* return this.service.getItems(user.customerId);
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
exports.CurrentUser = (0, common_1.createParamDecorator)((_data, context) => (0, utils_1.getRequest)(context).user);
|
|
22
|
+
//# sourceMappingURL=current-user.decorator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"current-user.decorator.js","sourceRoot":"","sources":["../../src/auth/current-user.decorator.ts"],"names":[],"mappings":";;;AAAA,2CAAwE;AAExE,mCAAqC;AAErC;;;;;;;;;;;;;;GAcG;AACU,QAAA,WAAW,GAAG,IAAA,6BAAoB,EAC7C,CAAC,KAAc,EAAE,OAAyB,EAAY,EAAE,CACtD,IAAA,kBAAU,EAAC,OAAO,CAAC,CAAC,IAAgB,CACvC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './auth.types';
|
|
2
|
+
export * from './auth.module';
|
|
3
|
+
export * from './jwt.service';
|
|
4
|
+
export * from './jwt-auth.guard';
|
|
5
|
+
export * from './optional-jwt-auth.guard';
|
|
6
|
+
export * from './current-user.decorator';
|
|
7
|
+
export * from './ownership';
|
|
8
|
+
export { getRequest, bearer, normalizeGid, normalizePhone, sha256, hashPhone, } from './utils';
|
|
9
|
+
export type { AuthRequest } from './utils';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,0BAA0B,CAAC;AACzC,cAAc,aAAa,CAAC;AAC5B,OAAO,EACL,UAAU,EACV,MAAM,EACN,YAAY,EACZ,cAAc,EACd,MAAM,EACN,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hashPhone = exports.sha256 = exports.normalizePhone = exports.normalizeGid = exports.bearer = exports.getRequest = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
tslib_1.__exportStar(require("./auth.types"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./auth.module"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./jwt.service"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./jwt-auth.guard"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./optional-jwt-auth.guard"), exports);
|
|
10
|
+
tslib_1.__exportStar(require("./current-user.decorator"), exports);
|
|
11
|
+
tslib_1.__exportStar(require("./ownership"), exports);
|
|
12
|
+
var utils_1 = require("./utils");
|
|
13
|
+
Object.defineProperty(exports, "getRequest", { enumerable: true, get: function () { return utils_1.getRequest; } });
|
|
14
|
+
Object.defineProperty(exports, "bearer", { enumerable: true, get: function () { return utils_1.bearer; } });
|
|
15
|
+
Object.defineProperty(exports, "normalizeGid", { enumerable: true, get: function () { return utils_1.normalizeGid; } });
|
|
16
|
+
Object.defineProperty(exports, "normalizePhone", { enumerable: true, get: function () { return utils_1.normalizePhone; } });
|
|
17
|
+
Object.defineProperty(exports, "sha256", { enumerable: true, get: function () { return utils_1.sha256; } });
|
|
18
|
+
Object.defineProperty(exports, "hashPhone", { enumerable: true, get: function () { return utils_1.hashPhone; } });
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":";;;;AAAA,uDAA6B;AAC7B,wDAA8B;AAC9B,wDAA8B;AAC9B,2DAAiC;AACjC,oEAA0C;AAC1C,mEAAyC;AACzC,sDAA4B;AAC5B,iCAOiB;AANf,mGAAA,UAAU,OAAA;AACV,+FAAA,MAAM,OAAA;AACN,qGAAA,YAAY,OAAA;AACZ,uGAAA,cAAc,OAAA;AACd,+FAAA,MAAM,OAAA;AACN,kGAAA,SAAS,OAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { AuthTokenService } from './jwt.service';
|
|
3
|
+
/**
|
|
4
|
+
* Requires a valid authentication token, and attaches the caller to the request.
|
|
5
|
+
*
|
|
6
|
+
* Apply with `@UseGuards(JwtAuthGuard)` on endpoints that read or change one
|
|
7
|
+
* customer's data. Endpoints that serve public data carry no guard.
|
|
8
|
+
*
|
|
9
|
+
* On success `request.user` holds an {@link AuthUser}, which handlers read via
|
|
10
|
+
* `@CurrentUser()`. Identity must come from there and never from the request
|
|
11
|
+
* body, query string or GraphQL arguments — a caller controls those.
|
|
12
|
+
*
|
|
13
|
+
* Note that this guard answers only "who is calling". It does not check that
|
|
14
|
+
* the records a request names belong to that caller; see `ownership.ts`.
|
|
15
|
+
*/
|
|
16
|
+
export declare class JwtAuthGuard implements CanActivate {
|
|
17
|
+
private readonly tokens;
|
|
18
|
+
constructor(tokens: AuthTokenService);
|
|
19
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=jwt-auth.guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-auth.guard.d.ts","sourceRoot":"","sources":["../../src/auth/jwt-auth.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,gBAAgB,EAGjB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,gBAAgB,EAIjB,MAAM,eAAe,CAAC;AAGvB;;;;;;;;;;;;GAYG;AACH,qBACa,YAAa,YAAW,WAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,gBAAgB;IAE/C,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAwD/D"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JwtAuthGuard = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const logger_1 = require("../common/logger");
|
|
7
|
+
const auth_types_1 = require("./auth.types");
|
|
8
|
+
const jwt_service_1 = require("./jwt.service");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
/**
|
|
11
|
+
* Requires a valid authentication token, and attaches the caller to the request.
|
|
12
|
+
*
|
|
13
|
+
* Apply with `@UseGuards(JwtAuthGuard)` on endpoints that read or change one
|
|
14
|
+
* customer's data. Endpoints that serve public data carry no guard.
|
|
15
|
+
*
|
|
16
|
+
* On success `request.user` holds an {@link AuthUser}, which handlers read via
|
|
17
|
+
* `@CurrentUser()`. Identity must come from there and never from the request
|
|
18
|
+
* body, query string or GraphQL arguments — a caller controls those.
|
|
19
|
+
*
|
|
20
|
+
* Note that this guard answers only "who is calling". It does not check that
|
|
21
|
+
* the records a request names belong to that caller; see `ownership.ts`.
|
|
22
|
+
*/
|
|
23
|
+
let JwtAuthGuard = class JwtAuthGuard {
|
|
24
|
+
tokens;
|
|
25
|
+
constructor(tokens) {
|
|
26
|
+
this.tokens = tokens;
|
|
27
|
+
}
|
|
28
|
+
async canActivate(context) {
|
|
29
|
+
const request = (0, utils_1.getRequest)(context);
|
|
30
|
+
const token = (0, utils_1.bearer)(request);
|
|
31
|
+
if (!token) {
|
|
32
|
+
throw new common_1.UnauthorizedException({
|
|
33
|
+
code: auth_types_1.AuthErrorCode.TOKEN_MISSING,
|
|
34
|
+
message: 'Authentication required',
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
request.user = await this.tokens.verifyAccess(token);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error instanceof jwt_service_1.TokenExpiredError) {
|
|
43
|
+
// Expected and routine — the client refreshes and retries, so this is
|
|
44
|
+
// not worth logging on every occurrence.
|
|
45
|
+
throw new common_1.UnauthorizedException({
|
|
46
|
+
code: auth_types_1.AuthErrorCode.TOKEN_EXPIRED,
|
|
47
|
+
message: 'Token has expired',
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (error instanceof jwt_service_1.TokenWrongTypeError) {
|
|
51
|
+
// Almost certainly a client bug: the refresh token was sent on a normal
|
|
52
|
+
// request. Distinguished from TOKEN_INVALID so it is obvious in a log.
|
|
53
|
+
logger_1.logger.warn('Wrong token type on a protected route', {
|
|
54
|
+
reason: error.message,
|
|
55
|
+
path: typeof request.url === 'string' ? request.url : undefined,
|
|
56
|
+
});
|
|
57
|
+
throw new common_1.UnauthorizedException({
|
|
58
|
+
code: auth_types_1.AuthErrorCode.TOKEN_WRONG_TYPE,
|
|
59
|
+
message: 'Invalid token',
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (error instanceof jwt_service_1.TokenInvalidError) {
|
|
63
|
+
// A bad signature, wrong issuer or malformed token is not something a
|
|
64
|
+
// healthy client produces. Log it — a rise here is worth noticing —
|
|
65
|
+
// but keep the reason out of the response so probing gains nothing.
|
|
66
|
+
logger_1.logger.warn('Rejected authentication token', {
|
|
67
|
+
reason: error.message,
|
|
68
|
+
path: typeof request.url === 'string' ? request.url : undefined,
|
|
69
|
+
});
|
|
70
|
+
throw new common_1.UnauthorizedException({
|
|
71
|
+
code: auth_types_1.AuthErrorCode.TOKEN_INVALID,
|
|
72
|
+
message: 'Invalid token',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
exports.JwtAuthGuard = JwtAuthGuard;
|
|
80
|
+
exports.JwtAuthGuard = JwtAuthGuard = tslib_1.__decorate([
|
|
81
|
+
(0, common_1.Injectable)(),
|
|
82
|
+
tslib_1.__metadata("design:paramtypes", [jwt_service_1.AuthTokenService])
|
|
83
|
+
], JwtAuthGuard);
|
|
84
|
+
//# sourceMappingURL=jwt-auth.guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-auth.guard.js","sourceRoot":"","sources":["../../src/auth/jwt-auth.guard.ts"],"names":[],"mappings":";;;;AAAA,2CAKwB;AACxB,6CAA0C;AAC1C,6CAA6C;AAC7C,+CAKuB;AACvB,mCAA6C;AAE7C;;;;;;;;;;;;GAYG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACM;IAA7B,YAA6B,MAAwB;QAAxB,WAAM,GAAN,MAAM,CAAkB;IAAG,CAAC;IAEzD,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,IAAA,kBAAU,EAAC,OAAO,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,IAAA,cAAM,EAAC,OAAO,CAAC,CAAC;QAE9B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,8BAAqB,CAAC;gBAC9B,IAAI,EAAE,0BAAa,CAAC,aAAa;gBACjC,OAAO,EAAE,yBAAyB;aACnC,CAAC,CAAC;QACL,CAAC;QAED,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACrD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,+BAAiB,EAAE,CAAC;gBACvC,sEAAsE;gBACtE,yCAAyC;gBACzC,MAAM,IAAI,8BAAqB,CAAC;oBAC9B,IAAI,EAAE,0BAAa,CAAC,aAAa;oBACjC,OAAO,EAAE,mBAAmB;iBAC7B,CAAC,CAAC;YACL,CAAC;YAED,IAAI,KAAK,YAAY,iCAAmB,EAAE,CAAC;gBACzC,wEAAwE;gBACxE,uEAAuE;gBACvE,eAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE;oBACnD,MAAM,EAAE,KAAK,CAAC,OAAO;oBACrB,IAAI,EAAE,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;iBAChE,CAAC,CAAC;gBAEH,MAAM,IAAI,8BAAqB,CAAC;oBAC9B,IAAI,EAAE,0BAAa,CAAC,gBAAgB;oBACpC,OAAO,EAAE,eAAe;iBACzB,CAAC,CAAC;YACL,CAAC;YAED,IAAI,KAAK,YAAY,+BAAiB,EAAE,CAAC;gBACvC,sEAAsE;gBACtE,oEAAoE;gBACpE,oEAAoE;gBACpE,eAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE;oBAC3C,MAAM,EAAE,KAAK,CAAC,OAAO;oBACrB,IAAI,EAAE,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS;iBAChE,CAAC,CAAC;gBAEH,MAAM,IAAI,8BAAqB,CAAC;oBAC9B,IAAI,EAAE,0BAAa,CAAC,aAAa;oBACjC,OAAO,EAAE,eAAe;iBACzB,CAAC,CAAC;YACL,CAAC;YAED,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAA;AA3DY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;6CAE0B,8BAAgB;GAD1C,YAAY,CA2DxB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { JwtService as NestJwtService } from '@nestjs/jwt';
|
|
2
|
+
import { AuthModuleOptions, AuthUser } from './auth.types';
|
|
3
|
+
/** Thrown when a token is well-formed and correctly signed but has expired. */
|
|
4
|
+
export declare class TokenExpiredError extends Error {
|
|
5
|
+
}
|
|
6
|
+
/** Thrown when a token fails any other check. */
|
|
7
|
+
export declare class TokenInvalidError extends Error {
|
|
8
|
+
}
|
|
9
|
+
/** Thrown when a refresh token is used as an access token, or the reverse. */
|
|
10
|
+
export declare class TokenWrongTypeError extends Error {
|
|
11
|
+
}
|
|
12
|
+
/** A freshly issued pair. */
|
|
13
|
+
export interface TokenPair {
|
|
14
|
+
accessToken: string;
|
|
15
|
+
accessExpiresAt: Date;
|
|
16
|
+
refreshToken: string;
|
|
17
|
+
refreshExpiresAt: Date;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Signs and verifies the authentication tokens this service issues.
|
|
21
|
+
*
|
|
22
|
+
* Two kinds, distinguished by a `typ` claim that is checked on every verify:
|
|
23
|
+
*
|
|
24
|
+
* - **access** (30 min) — sent on every protected request.
|
|
25
|
+
* - **refresh** (7 days) — sent only to `/auth/refresh`, to mint a new access
|
|
26
|
+
* token without going back to Shopify.
|
|
27
|
+
*
|
|
28
|
+
* The refresh token carries the same identity claims as an access token, which is
|
|
29
|
+
* what lets renewal be a purely local operation: no Shopify call, so renewal
|
|
30
|
+
* costs nothing against Shopify's rate limit.
|
|
31
|
+
*
|
|
32
|
+
* Fixed to RS256, and the algorithm is pinned on verify rather than read from the
|
|
33
|
+
* token. That is deliberate: the JWT spec allows `alg: none`, and a verifier that
|
|
34
|
+
* trusts the token's own header can also be tricked into validating an RS256
|
|
35
|
+
* token as HS256 using the public key as the HMAC secret. Both attacks fail
|
|
36
|
+
* against an explicit allow-list of one algorithm.
|
|
37
|
+
*/
|
|
38
|
+
export declare class AuthTokenService {
|
|
39
|
+
private readonly jwt;
|
|
40
|
+
private readonly options;
|
|
41
|
+
private readonly ttlSeconds;
|
|
42
|
+
private readonly refreshTtlSeconds;
|
|
43
|
+
constructor(jwt: NestJwtService, options: AuthModuleOptions);
|
|
44
|
+
/**
|
|
45
|
+
* Issue an access and refresh token for a customer whose identity Shopify has
|
|
46
|
+
* already confirmed.
|
|
47
|
+
*
|
|
48
|
+
* @param customerId - Shopify customer GID, taken from Shopify's response.
|
|
49
|
+
* @param phone - The customer's phone number as Shopify holds it, if any.
|
|
50
|
+
* Stored as a hash so a `mobile` value in a later request can be checked
|
|
51
|
+
* against it without the number appearing in the token.
|
|
52
|
+
* @param maxExpiresAt - Optional upper bound on both expiries, normally an
|
|
53
|
+
* upstream credential's own expiry, so neither token outlives it.
|
|
54
|
+
*/
|
|
55
|
+
issuePair(customerId: string, phone?: string | null, maxExpiresAt?: Date | string | null): Promise<TokenPair>;
|
|
56
|
+
/**
|
|
57
|
+
* Issue a replacement access token from a verified refresh token.
|
|
58
|
+
*
|
|
59
|
+
* The refresh token itself is **not** replaced: its expiry is the absolute end
|
|
60
|
+
* of the session (see `refreshTtlSeconds`). The caller keeps using the same one
|
|
61
|
+
* until it expires, then logs in again.
|
|
62
|
+
*/
|
|
63
|
+
issueAccessToken(user: AuthUser, maxExpiresAt?: Date | string | null): Promise<{
|
|
64
|
+
token: string;
|
|
65
|
+
expiresAt: Date;
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* Verify an access token and return the caller it identifies.
|
|
69
|
+
*
|
|
70
|
+
* @throws {TokenExpiredError} past `exp` — the client should refresh and retry.
|
|
71
|
+
* @throws {TokenWrongTypeError} a refresh token was sent instead.
|
|
72
|
+
* @throws {TokenInvalidError} anything else.
|
|
73
|
+
*/
|
|
74
|
+
verifyAccess(token: string): Promise<AuthUser>;
|
|
75
|
+
/**
|
|
76
|
+
* Verify a refresh token and return the caller it identifies.
|
|
77
|
+
*
|
|
78
|
+
* @throws {TokenExpiredError} the session has reached its absolute end; the
|
|
79
|
+
* customer has to log in again.
|
|
80
|
+
* @throws {TokenWrongTypeError} an access token was sent instead.
|
|
81
|
+
* @throws {TokenInvalidError} anything else.
|
|
82
|
+
*/
|
|
83
|
+
verifyRefresh(token: string): Promise<AuthUser>;
|
|
84
|
+
private verify;
|
|
85
|
+
private sign;
|
|
86
|
+
private signFromHash;
|
|
87
|
+
/**
|
|
88
|
+
* Work out the `exp` claim: the given lifetime, shortened if an upstream
|
|
89
|
+
* credential expires sooner.
|
|
90
|
+
*/
|
|
91
|
+
private resolveExpiry;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=jwt.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt.service.d.ts","sourceRoot":"","sources":["../../src/auth/jwt.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAEL,iBAAiB,EACjB,QAAQ,EAGT,MAAM,cAAc,CAAC;AAGtB,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAE/C,iDAAiD;AACjD,qBAAa,iBAAkB,SAAQ,KAAK;CAAG;AAE/C,8EAA8E;AAC9E,qBAAa,mBAAoB,SAAQ,KAAK;CAAG;AAKjD,6BAA6B;AAC7B,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,IAAI,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,IAAI,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,gBAAgB;IAKzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACE,OAAO,CAAC,QAAQ,CAAC,OAAO;IALhD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGxB,GAAG,EAAE,cAAc,EACG,OAAO,EAAE,iBAAiB;IAOnE;;;;;;;;;;OAUG;IACG,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,EACrB,YAAY,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAClC,OAAO,CAAC,SAAS,CAAC;IAoBrB;;;;;;OAMG;IACG,gBAAgB,CACpB,IAAI,EAAE,QAAQ,EACd,YAAY,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAClC,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAA;KAAE,CAAC;IAU9C;;;;;;OAMG;IACG,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIpD;;;;;;;OAOG;IACG,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;YAIvC,MAAM;IA2CpB,OAAO,CAAC,IAAI;YAgBE,YAAY;IA8B1B;;;OAGG;IACH,OAAO,CAAC,aAAa;CActB"}
|