Skip to content

Commit cb8fcd5

Browse files
feat: add support for showing tenant join approval pending screen
1 parent 8dc698c commit cb8fcd5

File tree

10 files changed

+246
-98
lines changed

10 files changed

+246
-98
lines changed

packages/tenant-enrollment-nodejs/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"directory": "packages/tenant-enrollment-nodejs"
1010
},
1111
"scripts": {
12-
"build": "tsup src/index.ts --format cjs,esm --dts",
12+
"build": "vite build && npm run pretty",
1313
"pretty": "npx pretty-quick .",
1414
"pretty-check": "npx pretty-quick --check .",
1515
"test": "TEST_MODE=testing vitest run --pool=forks --passWithNoTests"
@@ -19,9 +19,9 @@
1919
"plugin",
2020
"supertokens"
2121
],
22-
"dependencies": {},
2322
"peerDependencies": {
24-
"supertokens-node": ">=23.0.0"
23+
"supertokens-node": ">=23.0.0",
24+
"@supertokens-plugins/tenants-nodejs": "*"
2525
},
2626
"devDependencies": {
2727
"@shared/eslint": "*",

packages/tenant-enrollment-nodejs/src/plugin.ts

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -98,25 +98,7 @@ export const init = createPluginInitFunction<
9898
};
9999
}
100100

101-
const response = await originalImplementation.signUp(input);
102-
if (response.status !== "OK") {
103-
return response;
104-
}
105-
106-
const { wasAddedToTenant, reason: tenantJoiningReason } =
107-
await implementation.handleTenantJoiningApproval(
108-
response.user,
109-
input.tenantId,
110-
associateLoginMethodDef,
111-
sendEmail,
112-
getAppUrlDef(appInfo, undefined, input.userContext),
113-
input.userContext,
114-
);
115-
return {
116-
...response,
117-
wasAddedToTenant,
118-
reason: tenantJoiningReason,
119-
};
101+
return originalImplementation.signUp(input);
120102
},
121103
};
122104
},
@@ -135,7 +117,30 @@ export const init = createPluginInitFunction<
135117
};
136118
}
137119

138-
return response;
120+
logDebugMessage(`Got response status for signup: ${response.status}`);
121+
if (response.status !== "OK") {
122+
return response;
123+
}
124+
125+
logDebugMessage("Going ahead with checking tenant joining approval");
126+
const { wasAddedToTenant, reason: tenantJoiningReason } =
127+
await implementation.handleTenantJoiningApproval(
128+
response.user,
129+
input.tenantId,
130+
associateLoginMethodDef,
131+
sendEmail,
132+
getAppUrlDef(appInfo, undefined, input.userContext),
133+
input.userContext,
134+
);
135+
logDebugMessage(`wasAdded: ${wasAddedToTenant}`);
136+
logDebugMessage(`reason: ${tenantJoiningReason}`);
137+
return {
138+
status: "PENDING_APPROVAL",
139+
wasAddedToTenant,
140+
reason: tenantJoiningReason,
141+
};
142+
143+
// return response;
139144
},
140145
};
141146
},
@@ -297,11 +302,11 @@ export const init = createPluginInitFunction<
297302
input.tenantId,
298303
deviceInfo.phoneNumber !== undefined
299304
? {
300-
phoneNumber: deviceInfo.phoneNumber!,
301-
}
305+
phoneNumber: deviceInfo.phoneNumber!,
306+
}
302307
: {
303-
email: deviceInfo.email!,
304-
},
308+
email: deviceInfo.email!,
309+
},
305310
);
306311
const isSignUp = accountInfoResponse.length === 0;
307312

packages/tenant-enrollment-nodejs/src/recipeImplementation.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { User } from 'supertokens-node';
2-
import { OverrideableTenantFunctionImplementation, SuperTokensPluginTenantEnrollmentPluginConfig } from './types';
1+
import { User } from "supertokens-node";
2+
import { OverrideableTenantFunctionImplementation, SuperTokensPluginTenantEnrollmentPluginConfig } from "./types";
33
import {
44
assignRoleToUserInTenant,
55
AssociateAllLoginMethodsOfUserWithTenant,
66
SendPluginEmail,
7-
} from '@supertokens-plugins/tenants-nodejs';
8-
import { ROLES } from '@shared/tenants';
9-
import SuperTokens from 'supertokens-node';
10-
import { UserContext } from 'supertokens-node/lib/build/types';
7+
} from "@supertokens-plugins/tenants-nodejs";
8+
import { ROLES } from "@shared/tenants";
9+
import SuperTokens from "supertokens-node";
10+
import { UserContext } from "supertokens-node/lib/build/types";
1111

1212
export const getOverrideableTenantFunctionImplementation = (
1313
config: SuperTokensPluginTenantEnrollmentPluginConfig,
@@ -23,7 +23,7 @@ export const getOverrideableTenantFunctionImplementation = (
2323
*/
2424

2525
// Skip this for the public tenant
26-
if (tenantId === 'public') {
26+
if (tenantId === "public") {
2727
return {
2828
canJoin: true,
2929
reason: undefined,
@@ -35,21 +35,21 @@ export const getOverrideableTenantFunctionImplementation = (
3535
if (implementation.isTenantInviteOnly(tenantId)) {
3636
return {
3737
canJoin: false,
38-
reason: 'INVITE_ONLY',
38+
reason: "INVITE_ONLY",
3939
};
4040
}
4141

4242
let canJoin = false;
4343
let reason = undefined;
44-
if (emailOrThirdPartyId.type === 'email') {
44+
if (emailOrThirdPartyId.type === "email") {
4545
canJoin = implementation.isMatchingEmailDomain(tenantId, emailOrThirdPartyId.email);
4646
if (!canJoin) {
47-
reason = 'EMAIL_DOMAIN_NOT_ALLOWED';
47+
reason = "EMAIL_DOMAIN_NOT_ALLOWED";
4848
}
49-
} else if (emailOrThirdPartyId.type === 'thirdParty') {
49+
} else if (emailOrThirdPartyId.type === "thirdParty") {
5050
canJoin = implementation.isApprovedIdPProvider(tenantId, emailOrThirdPartyId.thirdPartyId);
5151
if (!canJoin) {
52-
reason = 'IDP_NOT_ALLOWED';
52+
reason = "IDP_NOT_ALLOWED";
5353
}
5454
}
5555

@@ -79,7 +79,7 @@ export const getOverrideableTenantFunctionImplementation = (
7979
* @param associateLoginMethodDef - The function to associate the login methods of the user with the tenant
8080
*/
8181
// Skip this for the public tenant
82-
if (tenantId === 'public') {
82+
if (tenantId === "public") {
8383
return {
8484
wasAddedToTenant: true,
8585
reason: undefined,
@@ -99,11 +99,11 @@ export const getOverrideableTenantFunctionImplementation = (
9999
// and return.
100100
await associateLoginMethodDef(tenantId, user.id);
101101

102-
await implementation.sendTenantJoiningRequestEmail(tenantId, user, appUrl, sendEmail, userContext);
102+
// await implementation.sendTenantJoiningRequestEmail(tenantId, user, appUrl, sendEmail, userContext);
103103

104104
return {
105105
wasAddedToTenant: false,
106-
reason: 'REQUIRES_APPROVAL',
106+
reason: "REQUIRES_APPROVAL",
107107
};
108108
},
109109
isTenantInviteOnly: (tenantId) => {
@@ -113,10 +113,10 @@ export const getOverrideableTenantFunctionImplementation = (
113113
return config.requiresApprovalTenants?.includes(tenantId) ?? false;
114114
},
115115
isApprovedIdPProvider: (thirdPartyId) => {
116-
return thirdPartyId.startsWith('boxy-saml');
116+
return thirdPartyId.startsWith("boxy-saml");
117117
},
118118
isMatchingEmailDomain: (tenantId, email) => {
119-
const emailDomain = email.split('@');
119+
const emailDomain = email.split("@");
120120
if (emailDomain.length !== 2) {
121121
return false;
122122
}
@@ -150,10 +150,10 @@ export const getOverrideableTenantFunctionImplementation = (
150150
.map(async (email) => {
151151
await sendEmail(
152152
{
153-
type: 'TENANT_REQUEST_APPROVAL',
153+
type: "TENANT_REQUEST_APPROVAL",
154154
email,
155155
tenantId,
156-
senderEmail: user.emails[0],
156+
senderEmail: user.emails[0]!,
157157
appUrl,
158158
},
159159
userContext,
@@ -162,7 +162,7 @@ export const getOverrideableTenantFunctionImplementation = (
162162
);
163163
},
164164
getUserIdsInTenantWithRole: async (tenantId, role) => {
165-
throw new Error('Not implemented');
165+
throw new Error("Not implemented");
166166
},
167167
};
168168

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/// <reference types='vitest' />
2+
import { defineConfig } from "vite";
3+
import dts from "vite-plugin-dts";
4+
import peerDepsExternal from "rollup-plugin-peer-deps-external";
5+
import * as path from "path";
6+
import packageJson from "../tenants-nodejs/package.json";
7+
8+
export default defineConfig(() => ({
9+
root: __dirname,
10+
plugins: [
11+
dts({
12+
entryRoot: "src",
13+
tsconfigPath: path.join(__dirname, "tsconfig.json"),
14+
}),
15+
peerDepsExternal(),
16+
],
17+
build: {
18+
outDir: "./dist",
19+
emptyOutDir: true,
20+
commonjsOptions: {
21+
transformMixedEsModules: true,
22+
},
23+
lib: {
24+
// Could also be a dictionary or array of multiple entry points.
25+
entry: "src/index.ts",
26+
name: packageJson.name,
27+
fileName: "index",
28+
// Change this to the formats you want to support.
29+
// Don't forget to update your package.json as well.
30+
formats: ["es" as const, "cjs" as const],
31+
},
32+
rollupOptions: {
33+
// External packages that should not be bundled into your library.
34+
external: [],
35+
},
36+
},
37+
}));
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
declare module "*.module.css" {
2+
const classes: { [key: string]: string };
3+
export default classes;
4+
}
5+
6+
declare module "*.module.scss" {
7+
const classes: { [key: string]: string };
8+
export default classes;
9+
}
10+
11+
declare module "*.module.sass" {
12+
const classes: { [key: string]: string };
13+
export default classes;
14+
}
15+
16+
declare module "*.module.less" {
17+
const classes: { [key: string]: string };
18+
export default classes;
19+
}
20+
21+
declare module "*.module.styl" {
22+
const classes: { [key: string]: string };
23+
export default classes;
24+
}
25+
26+
declare module "*.css" {
27+
const css: string;
28+
export default css;
29+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
.awaitingApprovalMessageContainer {
2+
.header {
3+
font-weight: 700;
4+
font-size: 28px;
5+
line-height: 36px;
6+
letter-spacing: -0.12px;
7+
color: var(--neutral-color-neutral-12);
8+
margin: 0 0 16px 0;
9+
}
10+
11+
.messageContainer {
12+
box-shadow: 0px 1.5px 2px 0px rgba(0, 0, 0, 0.133) inset;
13+
border: 1px solid var(--neutral-color-neutral-6);
14+
background-color: #f9f9f8;
15+
border-radius: 12px;
16+
padding: 14px;
17+
18+
font-weight: 400;
19+
font-size: 14px;
20+
line-height: 20px;
21+
letter-spacing: 0px;
22+
23+
b {
24+
font-weight: 600;
25+
font-size: 14px;
26+
line-height: 20px;
27+
color: var(--neutral-color-neutral-11);
28+
}
29+
}
30+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Card } from "@shared/ui";
2+
import classNames from "classnames/bind";
3+
4+
import { usePluginContext } from "../../plugin";
5+
6+
import style from "./awaiting-approval.module.scss";
7+
const cx = classNames.bind(style);
8+
9+
export const AwaitingApproval = () => {
10+
const { t } = usePluginContext();
11+
12+
return (
13+
<Card className={cx("awaitingApprovalMessageContainer")}>
14+
<div className={cx("header")}>{t("PL_TE_JOIN_TENANT_AWAITING_APPROVAL_HEADER")}</div>
15+
<div className={cx("messageContainer")}>
16+
<div>
17+
{t("PL_TE_JOIN_TENANT_AWAITING_APPROVAL_MESSAGE")}{" "}
18+
<b>{t("PL_TE_JOIN_TENANT_AWAITING_APPROVAL_MESSAGE_HIGHLIGHT")}</b>
19+
</div>
20+
</div>
21+
</Card>
22+
);
23+
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { AwaitingApproval } from "./awaiting-approval";

0 commit comments

Comments
 (0)