@reactionary/core 0.12.4 → 0.12.6
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/capabilities/identity.capability.js +29 -0
- package/package.json +1 -1
- package/schemas/errors/identity-conflict.error.js +8 -0
- package/schemas/errors/index.js +1 -0
- package/schemas/models/identity-provider.model.js +26 -0
- package/schemas/models/index.js +1 -0
- package/schemas/mutations/identity.mutation.js +13 -1
- package/schemas/queries/identity.query.js +2 -0
- package/schemas/session.schema.js +7 -1
- package/src/capabilities/identity.capability.d.ts +20 -2
- package/src/schemas/errors/identity-conflict.error.d.ts +7 -0
- package/src/schemas/errors/index.d.ts +1 -0
- package/src/schemas/models/identity-provider.model.d.ts +23 -0
- package/src/schemas/models/index.d.ts +1 -0
- package/src/schemas/mutations/identity.mutation.d.ts +17 -0
- package/src/schemas/queries/identity.query.d.ts +2 -0
- package/src/schemas/session.schema.d.ts +18 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseCapability } from "./base.capability.js";
|
|
2
|
+
const PENDING_EXTERNAL_LOGIN_TTL_MS = 10 * 60 * 1e3;
|
|
2
3
|
class IdentityCapability extends BaseCapability {
|
|
3
4
|
getResourceName() {
|
|
4
5
|
return "identity";
|
|
@@ -7,6 +8,34 @@ class IdentityCapability extends BaseCapability {
|
|
|
7
8
|
this.context.session.identityContext.lastUpdated = /* @__PURE__ */ new Date();
|
|
8
9
|
this.context.session.identityContext.identity = identity;
|
|
9
10
|
}
|
|
11
|
+
setPendingExternalLogin(provider, state, data) {
|
|
12
|
+
this.context.session.identityContext.pendingExternalLogin = {
|
|
13
|
+
provider,
|
|
14
|
+
state,
|
|
15
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
16
|
+
...data ? { data } : {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Returns the pending login, and clears it, when provider and state match a login started in this session within
|
|
21
|
+
* the last 10 minutes. Returns undefined otherwise. A pending login can only be taken once.
|
|
22
|
+
*/
|
|
23
|
+
takePendingExternalLogin(provider, state) {
|
|
24
|
+
const pending = this.context.session.identityContext.pendingExternalLogin;
|
|
25
|
+
delete this.context.session.identityContext.pendingExternalLogin;
|
|
26
|
+
if (!pending || !state) {
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
const fresh = Date.now() - new Date(pending.createdAt).getTime() <= PENDING_EXTERNAL_LOGIN_TTL_MS;
|
|
30
|
+
if (!fresh || pending.provider !== provider || pending.state !== state) {
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
return { data: pending.data ?? {} };
|
|
34
|
+
}
|
|
35
|
+
/** Returns true, and clears the pending login, when provider and state match a login started in this session within the last 10 minutes. */
|
|
36
|
+
consumePendingExternalLogin(provider, state) {
|
|
37
|
+
return this.takePendingExternalLogin(provider, state) !== void 0;
|
|
38
|
+
}
|
|
10
39
|
}
|
|
11
40
|
export {
|
|
12
41
|
IdentityCapability
|
package/package.json
CHANGED
package/schemas/errors/index.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { BaseModelSchema } from "./base.model.js";
|
|
3
|
+
const IdentityProviderIdentifierSchema = z.looseObject({
|
|
4
|
+
key: z.string()
|
|
5
|
+
});
|
|
6
|
+
const IdentityProviderSchema = BaseModelSchema.extend({
|
|
7
|
+
identifier: IdentityProviderIdentifierSchema,
|
|
8
|
+
displayName: z.string(),
|
|
9
|
+
brand: z.string().optional().meta({ description: "A neutral slug for the identity provider's brand, such as google, apple, amazon, facebook, microsoft or email, so a storefront can choose its own icon. Absent when the brand is not known." }),
|
|
10
|
+
flow: z.enum(["credentials", "redirect"]).meta({ description: "credentials: the shopper types a username and password into the storefront. redirect: the shopper is sent to an external identity provider and returns with a code." })
|
|
11
|
+
});
|
|
12
|
+
const ExternalLoginRedirectSchema = z.looseObject({
|
|
13
|
+
location: z.url().meta({ description: "Where to send the shopper to authenticate with the external identity provider." })
|
|
14
|
+
});
|
|
15
|
+
const PASSWORD_IDENTITY_PROVIDER = IdentityProviderSchema.parse({
|
|
16
|
+
identifier: { key: "password" },
|
|
17
|
+
displayName: "Email and password",
|
|
18
|
+
brand: "email",
|
|
19
|
+
flow: "credentials"
|
|
20
|
+
});
|
|
21
|
+
export {
|
|
22
|
+
ExternalLoginRedirectSchema,
|
|
23
|
+
IdentityProviderIdentifierSchema,
|
|
24
|
+
IdentityProviderSchema,
|
|
25
|
+
PASSWORD_IDENTITY_PROVIDER
|
|
26
|
+
};
|
package/schemas/models/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./category.model.js";
|
|
|
6
6
|
export * from "./currency.model.js";
|
|
7
7
|
export * from "./identifiers.model.js";
|
|
8
8
|
export * from "./identity.model.js";
|
|
9
|
+
export * from "./identity-provider.model.js";
|
|
9
10
|
export * from "./inventory.model.js";
|
|
10
11
|
export * from "./payment.model.js";
|
|
11
12
|
export * from "./price.model.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { BaseMutationSchema } from "./base.mutation.js";
|
|
3
|
+
import { IdentityProviderIdentifierSchema } from "../models/identity-provider.model.js";
|
|
3
4
|
const IdentityMutationLoginSchema = BaseMutationSchema.extend({
|
|
4
5
|
username: z.string(),
|
|
5
6
|
password: z.string()
|
|
@@ -9,8 +10,19 @@ const IdentityMutationRegisterSchema = BaseMutationSchema.extend({
|
|
|
9
10
|
username: z.string(),
|
|
10
11
|
password: z.string()
|
|
11
12
|
});
|
|
13
|
+
const IdentityMutationStartExternalLoginSchema = BaseMutationSchema.extend({
|
|
14
|
+
provider: IdentityProviderIdentifierSchema,
|
|
15
|
+
callbackUrl: z.url().meta({ description: "The storefront URL the identity provider returns the shopper to. Must be registered with the identity provider." })
|
|
16
|
+
});
|
|
17
|
+
const IdentityMutationCompleteExternalLoginSchema = BaseMutationSchema.extend({
|
|
18
|
+
provider: IdentityProviderIdentifierSchema,
|
|
19
|
+
callbackUrl: z.url().meta({ description: "The same callbackUrl that was passed to startExternalLogin." }),
|
|
20
|
+
callbackParameters: z.record(z.string(), z.string()).meta({ description: "The query parameters the identity provider appended to the callback URL, passed through unchanged." })
|
|
21
|
+
});
|
|
12
22
|
export {
|
|
23
|
+
IdentityMutationCompleteExternalLoginSchema,
|
|
13
24
|
IdentityMutationLoginSchema,
|
|
14
25
|
IdentityMutationLogoutSchema,
|
|
15
|
-
IdentityMutationRegisterSchema
|
|
26
|
+
IdentityMutationRegisterSchema,
|
|
27
|
+
IdentityMutationStartExternalLoginSchema
|
|
16
28
|
};
|
|
@@ -11,7 +11,13 @@ const LanguageContextSchema = z.looseObject({
|
|
|
11
11
|
});
|
|
12
12
|
const IdentityContextSchema = z.looseObject({
|
|
13
13
|
identity: IdentitySchema,
|
|
14
|
-
lastUpdated: z.coerce.date()
|
|
14
|
+
lastUpdated: z.coerce.date(),
|
|
15
|
+
pendingExternalLogin: z.looseObject({
|
|
16
|
+
provider: z.string(),
|
|
17
|
+
state: z.string(),
|
|
18
|
+
createdAt: z.coerce.date(),
|
|
19
|
+
data: z.record(z.string(), z.string()).optional().meta({ description: "Provider specific values that must survive the redirect, such as a PKCE code verifier and nonce." })
|
|
20
|
+
}).optional().meta({ description: "An external login that has been started but not completed. Binds the callback to the browser that started it." })
|
|
15
21
|
});
|
|
16
22
|
const SessionSchema = z.looseObject({
|
|
17
23
|
id: z.string().optional().meta({ description: "The id of the visitor's current session, set by the storefront. A long-lived visitor id belongs in marketingContext.identifier, not here." }),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { ExternalLoginRedirect, IdentityProvider } from "../schemas/models/identity-provider.model.js";
|
|
1
2
|
import type { Identity } from "../schemas/models/identity.model.js";
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
3
|
+
import type { IdentityConflictError } from "../schemas/errors/identity-conflict.error.js";
|
|
4
|
+
import type { IdentityMutationCompleteExternalLogin, IdentityMutationLogin, IdentityMutationLogout, IdentityMutationRegister, IdentityMutationStartExternalLogin } from "../schemas/mutations/identity.mutation.js";
|
|
5
|
+
import type { IdentityQueryProviders, IdentityQuerySelf } from "../schemas/queries/identity.query.js";
|
|
4
6
|
import type { Result } from "../schemas/result.js";
|
|
5
7
|
import { BaseCapability } from "./base.capability.js";
|
|
6
8
|
export declare abstract class IdentityCapability<TIdentity extends Identity = Identity> extends BaseCapability {
|
|
@@ -8,6 +10,22 @@ export declare abstract class IdentityCapability<TIdentity extends Identity = Id
|
|
|
8
10
|
abstract login(payload: IdentityMutationLogin): Promise<Result<TIdentity>>;
|
|
9
11
|
abstract logout(payload: IdentityMutationLogout): Promise<Result<TIdentity>>;
|
|
10
12
|
abstract register(payload: IdentityMutationRegister): Promise<Result<TIdentity>>;
|
|
13
|
+
/** The ways a shopper can sign in with this backend. */
|
|
14
|
+
abstract listProviders(payload: IdentityQueryProviders): Promise<Result<IdentityProvider[]>>;
|
|
15
|
+
/** Begins a redirect flow. The caller sends the shopper to the returned location. */
|
|
16
|
+
abstract startExternalLogin(payload: IdentityMutationStartExternalLogin): Promise<Result<ExternalLoginRedirect>>;
|
|
17
|
+
/** Completes a redirect flow from the parameters the identity provider appended to the callback URL. */
|
|
18
|
+
abstract completeExternalLogin(payload: IdentityMutationCompleteExternalLogin): Promise<Result<TIdentity, IdentityConflictError>>;
|
|
11
19
|
protected getResourceName(): string;
|
|
12
20
|
protected updateIdentityContext(identity: TIdentity): void;
|
|
21
|
+
protected setPendingExternalLogin(provider: string, state: string, data?: Record<string, string>): void;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the pending login, and clears it, when provider and state match a login started in this session within
|
|
24
|
+
* the last 10 minutes. Returns undefined otherwise. A pending login can only be taken once.
|
|
25
|
+
*/
|
|
26
|
+
protected takePendingExternalLogin(provider: string, state: string | undefined): {
|
|
27
|
+
data: Record<string, string>;
|
|
28
|
+
} | undefined;
|
|
29
|
+
/** Returns true, and clears the pending login, when provider and state match a login started in this session within the last 10 minutes. */
|
|
30
|
+
protected consumePendingExternalLogin(provider: string, state: string | undefined): boolean;
|
|
13
31
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
import type { InferType } from '../../zod-utils.js';
|
|
3
|
+
export declare const IdentityConflictErrorSchema: z.ZodObject<{
|
|
4
|
+
type: z.ZodLiteral<"IdentityConflict">;
|
|
5
|
+
message: z.ZodString;
|
|
6
|
+
}, z.core.$loose>;
|
|
7
|
+
export type IdentityConflictError = InferType<typeof IdentityConflictErrorSchema>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
import type { InferType } from '../../zod-utils.js';
|
|
3
|
+
export declare const IdentityProviderIdentifierSchema: z.ZodObject<{
|
|
4
|
+
key: z.ZodString;
|
|
5
|
+
}, z.core.$loose>;
|
|
6
|
+
export declare const IdentityProviderSchema: z.ZodObject<{
|
|
7
|
+
identifier: z.ZodObject<{
|
|
8
|
+
key: z.ZodString;
|
|
9
|
+
}, z.core.$loose>;
|
|
10
|
+
displayName: z.ZodString;
|
|
11
|
+
brand: z.ZodOptional<z.ZodString>;
|
|
12
|
+
flow: z.ZodEnum<{
|
|
13
|
+
credentials: "credentials";
|
|
14
|
+
redirect: "redirect";
|
|
15
|
+
}>;
|
|
16
|
+
}, z.core.$loose>;
|
|
17
|
+
export declare const ExternalLoginRedirectSchema: z.ZodObject<{
|
|
18
|
+
location: z.ZodURL;
|
|
19
|
+
}, z.core.$loose>;
|
|
20
|
+
export declare const PASSWORD_IDENTITY_PROVIDER: InferType<typeof IdentityProviderSchema>;
|
|
21
|
+
export type IdentityProviderIdentifier = InferType<typeof IdentityProviderIdentifierSchema>;
|
|
22
|
+
export type IdentityProvider = InferType<typeof IdentityProviderSchema>;
|
|
23
|
+
export type ExternalLoginRedirect = InferType<typeof ExternalLoginRedirectSchema>;
|
|
@@ -6,6 +6,7 @@ export * from './category.model.js';
|
|
|
6
6
|
export * from './currency.model.js';
|
|
7
7
|
export * from './identifiers.model.js';
|
|
8
8
|
export * from './identity.model.js';
|
|
9
|
+
export * from './identity-provider.model.js';
|
|
9
10
|
export * from './inventory.model.js';
|
|
10
11
|
export * from './payment.model.js';
|
|
11
12
|
export * from './price.model.js';
|
|
@@ -9,6 +9,23 @@ export declare const IdentityMutationRegisterSchema: z.ZodObject<{
|
|
|
9
9
|
username: z.ZodString;
|
|
10
10
|
password: z.ZodString;
|
|
11
11
|
}, z.core.$loose>;
|
|
12
|
+
export declare const IdentityMutationStartExternalLoginSchema: z.ZodObject<{
|
|
13
|
+
provider: z.ZodObject<{
|
|
14
|
+
key: z.ZodString;
|
|
15
|
+
}, z.core.$loose>;
|
|
16
|
+
callbackUrl: z.ZodURL;
|
|
17
|
+
}, z.core.$loose>;
|
|
18
|
+
export declare const IdentityMutationCompleteExternalLoginSchema: z.ZodObject<{
|
|
19
|
+
provider: z.ZodObject<{
|
|
20
|
+
key: z.ZodString;
|
|
21
|
+
}, z.core.$loose>;
|
|
22
|
+
callbackUrl: z.ZodURL;
|
|
23
|
+
callbackParameters: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
24
|
+
}, z.core.$loose>;
|
|
12
25
|
export type IdentityMutationLogin = InferType<typeof IdentityMutationLoginSchema>;
|
|
13
26
|
export type IdentityMutationLogout = InferType<typeof IdentityMutationLogoutSchema>;
|
|
14
27
|
export type IdentityMutationRegister = InferType<typeof IdentityMutationRegisterSchema>;
|
|
28
|
+
export type IdentityMutationStartExternalLogin = InferType<typeof IdentityMutationStartExternalLoginSchema>;
|
|
29
|
+
export type IdentityMutationCompleteExternalLogin = Omit<InferType<typeof IdentityMutationCompleteExternalLoginSchema>, 'callbackParameters'> & {
|
|
30
|
+
callbackParameters: Record<string, string>;
|
|
31
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type * as z from 'zod';
|
|
2
2
|
import type { InferType } from '../../zod-utils.js';
|
|
3
3
|
export declare const IdentityQuerySelfSchema: z.ZodObject<{}, z.core.$loose>;
|
|
4
|
+
export declare const IdentityQueryProvidersSchema: z.ZodObject<{}, z.core.$loose>;
|
|
5
|
+
export type IdentityQueryProviders = InferType<typeof IdentityQueryProvidersSchema>;
|
|
4
6
|
export type IdentityQuerySelf = InferType<typeof IdentityQuerySelfSchema>;
|
|
@@ -203,6 +203,12 @@ export declare const IdentityContextSchema: z.ZodObject<{
|
|
|
203
203
|
type: z.ZodLiteral<"Registered">;
|
|
204
204
|
}, z.core.$loose>], "type">;
|
|
205
205
|
lastUpdated: z.ZodCoercedDate<unknown>;
|
|
206
|
+
pendingExternalLogin: z.ZodOptional<z.ZodObject<{
|
|
207
|
+
provider: z.ZodString;
|
|
208
|
+
state: z.ZodString;
|
|
209
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
210
|
+
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
211
|
+
}, z.core.$loose>>;
|
|
206
212
|
}, z.core.$loose>;
|
|
207
213
|
export declare const SessionSchema: z.ZodObject<{
|
|
208
214
|
id: z.ZodOptional<z.ZodString>;
|
|
@@ -221,6 +227,12 @@ export declare const SessionSchema: z.ZodObject<{
|
|
|
221
227
|
type: z.ZodLiteral<"Registered">;
|
|
222
228
|
}, z.core.$loose>], "type">;
|
|
223
229
|
lastUpdated: z.ZodCoercedDate<unknown>;
|
|
230
|
+
pendingExternalLogin: z.ZodOptional<z.ZodObject<{
|
|
231
|
+
provider: z.ZodString;
|
|
232
|
+
state: z.ZodString;
|
|
233
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
234
|
+
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
235
|
+
}, z.core.$loose>>;
|
|
224
236
|
}, z.core.$loose>;
|
|
225
237
|
marketingContext: z.ZodDefault<z.ZodObject<{
|
|
226
238
|
identifier: z.ZodObject<{
|
|
@@ -258,6 +270,12 @@ export declare const RequestContextSchema: z.ZodObject<{
|
|
|
258
270
|
type: z.ZodLiteral<"Registered">;
|
|
259
271
|
}, z.core.$loose>], "type">;
|
|
260
272
|
lastUpdated: z.ZodCoercedDate<unknown>;
|
|
273
|
+
pendingExternalLogin: z.ZodOptional<z.ZodObject<{
|
|
274
|
+
provider: z.ZodString;
|
|
275
|
+
state: z.ZodString;
|
|
276
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
277
|
+
data: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
278
|
+
}, z.core.$loose>>;
|
|
261
279
|
}, z.core.$loose>;
|
|
262
280
|
marketingContext: z.ZodDefault<z.ZodObject<{
|
|
263
281
|
identifier: z.ZodObject<{
|