@reactionary/core 0.12.4 → 0.12.5

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.
@@ -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,19 @@ 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) {
12
+ this.context.session.identityContext.pendingExternalLogin = { provider, state, createdAt: /* @__PURE__ */ new Date() };
13
+ }
14
+ /** Returns true, and clears the pending login, when provider and state match a login started in this session within the last 10 minutes. */
15
+ consumePendingExternalLogin(provider, state) {
16
+ const pending = this.context.session.identityContext.pendingExternalLogin;
17
+ delete this.context.session.identityContext.pendingExternalLogin;
18
+ if (!pending || !state) {
19
+ return false;
20
+ }
21
+ const fresh = Date.now() - new Date(pending.createdAt).getTime() <= PENDING_EXTERNAL_LOGIN_TTL_MS;
22
+ return fresh && pending.provider === provider && pending.state === state;
23
+ }
10
24
  }
11
25
  export {
12
26
  IdentityCapability
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactionary/core",
3
- "version": "0.12.4",
3
+ "version": "0.12.5",
4
4
  "type": "module",
5
5
  "main": "./index.js",
6
6
  "types": "./src/index.d.ts",
@@ -0,0 +1,8 @@
1
+ import * as z from "zod";
2
+ const IdentityConflictErrorSchema = z.looseObject({
3
+ type: z.literal("IdentityConflict"),
4
+ message: z.string().nonempty()
5
+ });
6
+ export {
7
+ IdentityConflictErrorSchema
8
+ };
@@ -1,4 +1,5 @@
1
1
  export * from "./generic.error.js";
2
+ export * from "./identity-conflict.error.js";
2
3
  export * from "./invalid-input.error.js";
3
4
  export * from "./invalid-output.error.js";
4
5
  export * from "./not-found.error.js";
@@ -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
+ };
@@ -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
  };
@@ -1,5 +1,7 @@
1
1
  import { BaseQuerySchema } from "./base.query.js";
2
2
  const IdentityQuerySelfSchema = BaseQuerySchema.extend({});
3
+ const IdentityQueryProvidersSchema = BaseQuerySchema.extend({});
3
4
  export {
5
+ IdentityQueryProvidersSchema,
4
6
  IdentityQuerySelfSchema
5
7
  };
@@ -11,7 +11,12 @@ 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
+ }).optional().meta({ description: "An external login that has been started but not completed. Binds the callback to the browser that started it." })
15
20
  });
16
21
  const SessionSchema = z.looseObject({
17
22
  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 { IdentityMutationLogin, IdentityMutationLogout, IdentityMutationRegister } from "../schemas/mutations/identity.mutation.js";
3
- import type { IdentityQuerySelf } from "../schemas/queries/identity.query.js";
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,15 @@ 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): void;
22
+ /** Returns true, and clears the pending login, when provider and state match a login started in this session within the last 10 minutes. */
23
+ protected consumePendingExternalLogin(provider: string, state: string | undefined): boolean;
13
24
  }
@@ -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>;
@@ -1,4 +1,5 @@
1
1
  export * from './generic.error.js';
2
+ export * from './identity-conflict.error.js';
2
3
  export * from './invalid-input.error.js';
3
4
  export * from './invalid-output.error.js';
4
5
  export * from './not-found.error.js';
@@ -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,11 @@ 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
+ }, z.core.$loose>>;
206
211
  }, z.core.$loose>;
207
212
  export declare const SessionSchema: z.ZodObject<{
208
213
  id: z.ZodOptional<z.ZodString>;
@@ -221,6 +226,11 @@ export declare const SessionSchema: z.ZodObject<{
221
226
  type: z.ZodLiteral<"Registered">;
222
227
  }, z.core.$loose>], "type">;
223
228
  lastUpdated: z.ZodCoercedDate<unknown>;
229
+ pendingExternalLogin: z.ZodOptional<z.ZodObject<{
230
+ provider: z.ZodString;
231
+ state: z.ZodString;
232
+ createdAt: z.ZodCoercedDate<unknown>;
233
+ }, z.core.$loose>>;
224
234
  }, z.core.$loose>;
225
235
  marketingContext: z.ZodDefault<z.ZodObject<{
226
236
  identifier: z.ZodObject<{
@@ -258,6 +268,11 @@ export declare const RequestContextSchema: z.ZodObject<{
258
268
  type: z.ZodLiteral<"Registered">;
259
269
  }, z.core.$loose>], "type">;
260
270
  lastUpdated: z.ZodCoercedDate<unknown>;
271
+ pendingExternalLogin: z.ZodOptional<z.ZodObject<{
272
+ provider: z.ZodString;
273
+ state: z.ZodString;
274
+ createdAt: z.ZodCoercedDate<unknown>;
275
+ }, z.core.$loose>>;
261
276
  }, z.core.$loose>;
262
277
  marketingContext: z.ZodDefault<z.ZodObject<{
263
278
  identifier: z.ZodObject<{