From f84fbadfbd6df52384472c2d4a7a94335a884fbf Mon Sep 17 00:00:00 2001 From: Pete Nicholls Date: Thu, 11 Apr 2024 09:54:40 +1200 Subject: [PATCH 1/3] Remove unusued `authentication` from Pre User Registration --- src/mock/events/pre-user-registration.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mock/events/pre-user-registration.ts b/src/mock/events/pre-user-registration.ts index 545749a..0ed189b 100644 --- a/src/mock/events/pre-user-registration.ts +++ b/src/mock/events/pre-user-registration.ts @@ -2,7 +2,6 @@ import { define } from "../define"; import Auth0, { Connection } from "../../types"; import { user } from "../user"; import { request } from "../request"; -import { authentication } from "../authentication"; import { transaction } from "../transaction"; import { session } from "../session"; import { connection } from "../connection"; @@ -47,7 +46,6 @@ export const preUserRegistration = define( return { transaction: transactionValue, - authentication: authentication(), authorization: { roles: [], }, From acc4e3ae7643d71349f7601b63efb91d3e9a0e0b Mon Sep 17 00:00:00 2001 From: Pete Nicholls Date: Thu, 11 Apr 2024 10:08:46 +1200 Subject: [PATCH 2/3] Post User Registration --- README.md | 18 +++---- .../notify-slack-post-user-registration.js | 14 +++++ ...otify-slack-post-user-registration.test.js | 42 +++++++++++++++ src/mock/actions/index.ts | 1 + src/mock/actions/post-user-registration.ts | 44 +++++++++++++++ src/mock/api/index.ts | 1 + src/mock/api/post-user-registration.ts | 29 ++++++++++ src/mock/events/index.ts | 1 + src/mock/events/post-user-registration.ts | 53 +++++++++++++++++++ src/test/api/post-user-registration.test.ts | 22 ++++++++ src/types/api/index.d.ts | 1 + src/types/api/post-user-registration.d.ts | 9 ++++ src/types/events/index.d.ts | 1 + src/types/events/post-user-registration.d.ts | 19 +++++++ 14 files changed, 246 insertions(+), 9 deletions(-) create mode 100644 examples/notify-slack-post-user-registration.js create mode 100644 examples/notify-slack-post-user-registration.test.js create mode 100644 src/mock/actions/post-user-registration.ts create mode 100644 src/mock/api/post-user-registration.ts create mode 100644 src/mock/events/post-user-registration.ts create mode 100644 src/test/api/post-user-registration.test.ts create mode 100644 src/types/api/post-user-registration.d.ts create mode 100644 src/types/events/post-user-registration.d.ts diff --git a/README.md b/README.md index c097a99..3cc5a02 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,15 @@ This library provides you with the setup to test complex actions. Customise test The following [Flows](https://auth0.com/docs/customize/actions/flows-and-triggers) are supported: -| Flow | Support | -| ---------------------- | ------- | -| Login | ✓ | -| Machine to Machine | ✓ | -| Password Reset | ✓ | -| Pre User Registration | ✓ | -| Post User Registration | planned | -| Post Change Password | planned | -| Send Phone Message | planned | +| Flow | Support | +| ---------------------- | ----------------- | +| Login | ✓ from v0.1.0 | +| Machine to Machine | ✓ pending release | +| Password Reset | ✓ pending release | +| Pre User Registration | ✓ pending release | +| Post User Registration | ✓ pending release | +| Post Change Password | planned | +| Send Phone Message | planned | ## Getting started diff --git a/examples/notify-slack-post-user-registration.js b/examples/notify-slack-post-user-registration.js new file mode 100644 index 0000000..172774b --- /dev/null +++ b/examples/notify-slack-post-user-registration.js @@ -0,0 +1,14 @@ +/** + * Notify a Slack channel when a new user registers. + */ +exports.onExecutePostUserRegistration = async (event) => { + const payload = { + text: `New User: ${event.user.email}`, + }; + + await fetch(event.secrets.SLACK_WEBHOOK_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); +}; diff --git a/examples/notify-slack-post-user-registration.test.js b/examples/notify-slack-post-user-registration.test.js new file mode 100644 index 0000000..9d9a648 --- /dev/null +++ b/examples/notify-slack-post-user-registration.test.js @@ -0,0 +1,42 @@ +const test = require("node:test"); +const { strictEqual, deepStrictEqual } = require("node:assert"); +const { + onExecutePostUserRegistration, +} = require("./notify-slack-post-user-registration"); +const { nodeTestRunner } = require("@kilterset/auth0-actions-testing"); + +test("post user registration", async (t) => { + const { auth0, fetchMock } = await nodeTestRunner.actionTestSetup(t); + + await t.test("service is notified when user logs in", async (t) => { + fetchMock.mock("https://slack/hook", 201); + + const action = auth0.mock.actions.postUserRegistration({ + secrets: { SLACK_WEBHOOK_URL: "https://slack/hook" }, + user: auth0.mock.user({ email: "ellie@example.com" }), + }); + + await action.simulate(onExecutePostUserRegistration); + + const calls = fetchMock.calls(); + + strictEqual(calls.length, 1, "Expected 1 fetch call to be made."); + + const [url, options] = calls[0]; + + strictEqual(url, "https://slack/hook", "Unexpected URL"); + strictEqual(options.method, "POST", "Unexpected request method"); + + deepStrictEqual( + options.headers, + { "Content-Type": "application/json" }, + "Unexpected headers" + ); + + deepStrictEqual( + JSON.parse(options.body), + { text: "New User: ellie@example.com" }, + "Unexpected body" + ); + }); +}); diff --git a/src/mock/actions/index.ts b/src/mock/actions/index.ts index 7a393ce..3800520 100644 --- a/src/mock/actions/index.ts +++ b/src/mock/actions/index.ts @@ -1,4 +1,5 @@ export * from "./credentials-exchange"; export * from "./post-challenge"; export * from "./post-login"; +export * from "./post-user-registration"; export * from "./pre-user-registration"; diff --git a/src/mock/actions/post-user-registration.ts b/src/mock/actions/post-user-registration.ts new file mode 100644 index 0000000..f4d6d3f --- /dev/null +++ b/src/mock/actions/post-user-registration.ts @@ -0,0 +1,44 @@ +import { api, events } from ".."; +import Auth0 from "../../types"; +import { PostChallengeOptions } from "../api"; + +type Handler = ( + event: Auth0.Events.PostUserRegistration, + api: Auth0.API.PostUserRegistration +) => Promise; + +export function postUserRegistration({ + cache, + ...attributes +}: Parameters[0] & + Omit = {}) { + const event = events.postUserRegistration(attributes); + + const { implementation, state } = api.postUserRegistration({ cache }); + + async function simulate(handler: Handler) { + await handler(event, implementation); + } + + return new Proxy( + { + event, + simulate, + }, + { + get(target, prop) { + if (typeof prop !== "string") { + return; + } + + if (prop in target) { + return target[prop as keyof typeof target]; + } + + if (prop in state) { + return state[prop as keyof typeof state]; + } + }, + } + ); +} diff --git a/src/mock/api/index.ts b/src/mock/api/index.ts index fd06d61..41025bd 100644 --- a/src/mock/api/index.ts +++ b/src/mock/api/index.ts @@ -2,4 +2,5 @@ export * from "./cache"; export * from "./credentials-exchange"; export * from "./post-challenge"; export * from "./post-login"; +export * from "./post-user-registration"; export * from "./pre-user-registration"; diff --git a/src/mock/api/post-user-registration.ts b/src/mock/api/post-user-registration.ts new file mode 100644 index 0000000..c1c01ef --- /dev/null +++ b/src/mock/api/post-user-registration.ts @@ -0,0 +1,29 @@ +import Auth0 from "../../types"; +import { cache as mockCache } from "./cache"; + +export interface PostUserRegistrationOptions { + cache?: Record; +} + +export interface PostUserRegistrationState { + cache: Auth0.API.Cache; +} + +export function postUserRegistration({ + cache, +}: PostUserRegistrationOptions = {}) { + const apiCache = mockCache(cache); + + const state: PostUserRegistrationState = { + cache: apiCache, + }; + + const api: Auth0.API.PostUserRegistration = { + cache: apiCache, + }; + + return { + implementation: api, + state, + }; +} diff --git a/src/mock/events/index.ts b/src/mock/events/index.ts index d6c7394..b4c91ad 100644 --- a/src/mock/events/index.ts +++ b/src/mock/events/index.ts @@ -1,3 +1,4 @@ export * from "./post-login"; export * from "./credentials-exchange"; +export * from "./post-user-registration"; export * from "./pre-user-registration"; diff --git a/src/mock/events/post-user-registration.ts b/src/mock/events/post-user-registration.ts new file mode 100644 index 0000000..3fd7292 --- /dev/null +++ b/src/mock/events/post-user-registration.ts @@ -0,0 +1,53 @@ +import { define } from "../define"; +import Auth0, { Connection } from "../../types"; +import { user } from "../user"; +import { request } from "../request"; +import { transaction } from "../transaction"; +import { connection } from "../connection"; +import { chance } from "../chance"; +import { identity } from "../identity"; + +export const postUserRegistration = define( + ({ params }): Auth0.Events.PostUserRegistration => { + const tenantId = params.tenant?.id || chance.auth0().tenantId(); + const hostname = params.request?.hostname || `${tenantId}.auth0.com`; + + const connectionValue = params.connection + ? (params.connection as Connection) + : connection(); + + const identities = params.user?.identities || []; + + identities.splice( + 0, + 1, + identity({ + connection: connectionValue.name, + provider: connectionValue.strategy, + ...(identities[0] || {}), + }) + ); + + const userValue = user({ ...params.user, identities }); + + const requestValue = request({ + hostname, + ...params.request, + query: { + connection: connectionValue.name, + ...params.request?.query, + }, + }); + + const transactionValue = transaction(); + + return { + transaction: transactionValue, + connection: connectionValue, + tenant: { id: tenantId }, + request: requestValue, + user: userValue, + secrets: {}, + }; + } +); diff --git a/src/test/api/post-user-registration.test.ts b/src/test/api/post-user-registration.test.ts new file mode 100644 index 0000000..93d797f --- /dev/null +++ b/src/test/api/post-user-registration.test.ts @@ -0,0 +1,22 @@ +import test from "node:test"; +import { strictEqual, deepStrictEqual } from "node:assert"; +import { postUserRegistration } from "../../mock/api"; + +test("Post User Registration API", async (t) => { + await t.test("cache", async (t) => { + await t.test("can set cache", async (t) => { + const { implementation: api, state } = postUserRegistration(); + strictEqual(api.cache.set("location", "Ōtautahi").type, "success"); + deepStrictEqual(state.cache.get("location"), "Ōtautahi"); + }); + + await t.test("can get cache", async (t) => { + const { implementation: api, state } = postUserRegistration({ + cache: { location: "Ōtautahi" }, + }); + + strictEqual(state.cache.get("location"), "Ōtautahi"); + strictEqual(state.cache.get("nonexistent"), undefined); + }); + }); +}); diff --git a/src/types/api/index.d.ts b/src/types/api/index.d.ts index fd06d61..41025bd 100644 --- a/src/types/api/index.d.ts +++ b/src/types/api/index.d.ts @@ -2,4 +2,5 @@ export * from "./cache"; export * from "./credentials-exchange"; export * from "./post-challenge"; export * from "./post-login"; +export * from "./post-user-registration"; export * from "./pre-user-registration"; diff --git a/src/types/api/post-user-registration.d.ts b/src/types/api/post-user-registration.d.ts new file mode 100644 index 0000000..ced737e --- /dev/null +++ b/src/types/api/post-user-registration.d.ts @@ -0,0 +1,9 @@ +import { User } from "../user"; +import { Cache } from "./cache"; + +export interface PostUserRegistration { + /** + * Store and retrieve data that persists across executions. + */ + readonly cache: Cache; +} diff --git a/src/types/events/index.d.ts b/src/types/events/index.d.ts index 7a393ce..3800520 100644 --- a/src/types/events/index.d.ts +++ b/src/types/events/index.d.ts @@ -1,4 +1,5 @@ export * from "./credentials-exchange"; export * from "./post-challenge"; export * from "./post-login"; +export * from "./post-user-registration"; export * from "./pre-user-registration"; diff --git a/src/types/events/post-user-registration.d.ts b/src/types/events/post-user-registration.d.ts new file mode 100644 index 0000000..625a448 --- /dev/null +++ b/src/types/events/post-user-registration.d.ts @@ -0,0 +1,19 @@ +import { Authentication } from "../authentication"; +import { Client } from "../client"; +import { Connection } from "../connection"; +import { Request } from "../request"; +import { Transaction } from "../transaction"; +import { User } from "../user"; + +export interface PostUserRegistration { + connection: Connection; + request?: Request; + tenant: { + id: string; + }; + transaction?: Transaction; + user: User; + secrets: { + [key: string]: string; + }; +} From 3aa7935fc0776aaead7fe0ee52b93483db73c311 Mon Sep 17 00:00:00 2001 From: Pete Nicholls Date: Thu, 11 Apr 2024 10:09:49 +1200 Subject: [PATCH 3/3] Use chance.auth0().tenantId() consistently --- src/mock/events/post-challenge.ts | 2 +- src/mock/events/post-login.ts | 2 +- src/mock/events/pre-user-registration.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mock/events/post-challenge.ts b/src/mock/events/post-challenge.ts index c3bc7e6..4a05c49 100644 --- a/src/mock/events/post-challenge.ts +++ b/src/mock/events/post-challenge.ts @@ -13,7 +13,7 @@ import { identity } from "../identity"; export const postChallenge = define( ({ params }) => { - const tenantId = params.tenant?.id || chance.n(chance.word, 2).join("-"); + const tenantId = params.tenant?.id || chance.auth0().tenantId(); const hostname = params.request?.hostname || `${tenantId}.auth0.com`; const connectionValue = params.connection diff --git a/src/mock/events/post-login.ts b/src/mock/events/post-login.ts index 1f78997..0ae99d2 100644 --- a/src/mock/events/post-login.ts +++ b/src/mock/events/post-login.ts @@ -12,7 +12,7 @@ import { chance } from "../chance"; import { identity } from "../identity"; export const postLogin = define(({ params }) => { - const tenantId = params.tenant?.id || chance.n(chance.word, 2).join("-"); + const tenantId = params.tenant?.id || chance.auth0().tenantId(); const hostname = params.request?.hostname || `${tenantId}.auth0.com`; const connectionValue = params.connection diff --git a/src/mock/events/pre-user-registration.ts b/src/mock/events/pre-user-registration.ts index 0ed189b..2a55314 100644 --- a/src/mock/events/pre-user-registration.ts +++ b/src/mock/events/pre-user-registration.ts @@ -12,7 +12,7 @@ import { identity } from "../identity"; export const preUserRegistration = define( ({ params }) => { - const tenantId = params.tenant?.id || chance.n(chance.word, 2).join("-"); + const tenantId = params.tenant?.id || chance.auth0().tenantId(); const hostname = params.request?.hostname || `${tenantId}.auth0.com`; const connectionValue = params.connection