mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-12 04:15:30 +00:00
Add support for GCP IAP JIT account provisioning (#330)
* initial gcp iap implementation * gcp iap working * add docs for gcp iap * feedback * changelog
This commit is contained in:
parent
d5c4486664
commit
9227b3caba
11 changed files with 345 additions and 100 deletions
|
|
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Added
|
### Added
|
||||||
- Added copy button for filenames. [#328](https://github.com/sourcebot-dev/sourcebot/pull/328)
|
- Added copy button for filenames. [#328](https://github.com/sourcebot-dev/sourcebot/pull/328)
|
||||||
- Added development docker compose file. [#328](https://github.com/sourcebot-dev/sourcebot/pull/328)
|
- Added development docker compose file. [#328](https://github.com/sourcebot-dev/sourcebot/pull/328)
|
||||||
|
- Added GCP IAP JIT provisioning. [#330](https://github.com/sourcebot-dev/sourcebot/pull/330)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed issue with the symbol hover popover clipping at the top of the page. [#326](https://github.com/sourcebot-dev/sourcebot/pull/326)
|
- Fixed issue with the symbol hover popover clipping at the top of the page. [#326](https://github.com/sourcebot-dev/sourcebot/pull/326)
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,16 @@ Optional environment variables:
|
||||||
- `AUTH_EE_GOOGLE_CLIENT_ID`
|
- `AUTH_EE_GOOGLE_CLIENT_ID`
|
||||||
- `AUTH_EE_GOOGLE_CLIENT_SECRET`
|
- `AUTH_EE_GOOGLE_CLIENT_SECRET`
|
||||||
|
|
||||||
|
### GCP IAP
|
||||||
|
---
|
||||||
|
|
||||||
|
Custom provider built to enable automatic Sourcebot account registration/login when using GCP IAP.
|
||||||
|
|
||||||
|
**Required environment variables**
|
||||||
|
- `AUTH_EE_GCP_IAP_ENABLED`
|
||||||
|
- `AUTH_EE_GCP_IAP_AUDIENCE`
|
||||||
|
- This can be found by selecting the ⋮ icon next to the IAP-enabled backend service and pressing `Get JWT audience code`
|
||||||
|
|
||||||
### Okta
|
### Okta
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,8 @@ The following environment variables allow you to configure your Sourcebot deploy
|
||||||
| `AUTH_EE_OKTA_CLIENT_ID` | `-` | <p>The client ID for Okta SSO authentication.</p> |
|
| `AUTH_EE_OKTA_CLIENT_ID` | `-` | <p>The client ID for Okta SSO authentication.</p> |
|
||||||
| `AUTH_EE_OKTA_CLIENT_SECRET` | `-` | <p>The client secret for Okta SSO authentication.</p> |
|
| `AUTH_EE_OKTA_CLIENT_SECRET` | `-` | <p>The client secret for Okta SSO authentication.</p> |
|
||||||
| `AUTH_EE_OKTA_ISSUER` | `-` | <p>The issuer URL for Okta SSO authentication.</p> |
|
| `AUTH_EE_OKTA_ISSUER` | `-` | <p>The issuer URL for Okta SSO authentication.</p> |
|
||||||
|
| `AUTH_EE_GCP_IAP_ENABLED` | `false` | <p>When enabled, allows Sourcebot to automatically register/login from a successful GCP IAP redirect</p> |
|
||||||
|
| `AUTH_EE_GCP_IAP_AUDIENCE` | - | <p>The GCP IAP audience to use when verifying JWT tokens. Must be set to enable GCP IAP JIT provisioning</p> |
|
||||||
|
|
||||||
|
|
||||||
### Review Agent Environment Variables
|
### Review Agent Environment Variables
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@
|
||||||
"embla-carousel-react": "^8.3.0",
|
"embla-carousel-react": "^8.3.0",
|
||||||
"escape-string-regexp": "^5.0.0",
|
"escape-string-regexp": "^5.0.0",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
|
"google-auth-library": "^9.15.1",
|
||||||
"graphql": "^16.9.0",
|
"graphql": "^16.9.0",
|
||||||
"http-status-codes": "^2.3.0",
|
"http-status-codes": "^2.3.0",
|
||||||
"input-otp": "^1.4.2",
|
"input-otp": "^1.4.2",
|
||||||
|
|
|
||||||
26
packages/web/src/app/[domain]/components/gcpIapAuth.tsx
Normal file
26
packages/web/src/app/[domain]/components/gcpIapAuth.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { signIn } from "next-auth/react";
|
||||||
|
import { useEffect } from "react";
|
||||||
|
|
||||||
|
interface GcpIapAuthProps {
|
||||||
|
callbackUrl?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GcpIapAuth = ({ callbackUrl }: GcpIapAuthProps) => {
|
||||||
|
useEffect(() => {
|
||||||
|
signIn("gcp-iap", {
|
||||||
|
redirectTo: callbackUrl ?? "/"
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error("Error signing in with GCP IAP:", error);
|
||||||
|
});
|
||||||
|
}, [callbackUrl]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-lg">Signing in with Google Cloud IAP...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
@ -17,6 +17,7 @@ import { PendingApprovalCard } from "./components/pendingApproval";
|
||||||
import { hasEntitlement } from "@/features/entitlements/server";
|
import { hasEntitlement } from "@/features/entitlements/server";
|
||||||
import { getPublicAccessStatus } from "@/ee/features/publicAccess/publicAccess";
|
import { getPublicAccessStatus } from "@/ee/features/publicAccess/publicAccess";
|
||||||
import { env } from "@/env.mjs";
|
import { env } from "@/env.mjs";
|
||||||
|
import { GcpIapAuth } from "./components/gcpIapAuth";
|
||||||
|
|
||||||
interface LayoutProps {
|
interface LayoutProps {
|
||||||
children: React.ReactNode,
|
children: React.ReactNode,
|
||||||
|
|
@ -37,7 +38,12 @@ export default async function Layout({
|
||||||
if (!publicAccessEnabled) {
|
if (!publicAccessEnabled) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect('/login');
|
const ssoEntitlement = await hasEntitlement("sso");
|
||||||
|
if (ssoEntitlement && env.AUTH_EE_GCP_IAP_ENABLED && env.AUTH_EE_GCP_IAP_AUDIENCE) {
|
||||||
|
return <GcpIapAuth callbackUrl={`/${domain}`} />;
|
||||||
|
} else {
|
||||||
|
redirect('/login');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const membership = await prisma.userToOrg.findUnique({
|
const membership = await prisma.userToOrg.findUnique({
|
||||||
|
|
|
||||||
|
|
@ -5,21 +5,17 @@ import EmailProvider from "next-auth/providers/nodemailer";
|
||||||
import { PrismaAdapter } from "@auth/prisma-adapter"
|
import { PrismaAdapter } from "@auth/prisma-adapter"
|
||||||
import { prisma } from "@/prisma";
|
import { prisma } from "@/prisma";
|
||||||
import { env } from "@/env.mjs";
|
import { env } from "@/env.mjs";
|
||||||
import { OrgRole, User } from '@sourcebot/db';
|
import { User } from '@sourcebot/db';
|
||||||
import 'next-auth/jwt';
|
import 'next-auth/jwt';
|
||||||
import type { Provider } from "next-auth/providers";
|
import type { Provider } from "next-auth/providers";
|
||||||
import { verifyCredentialsRequestSchema } from './lib/schemas';
|
import { verifyCredentialsRequestSchema } from './lib/schemas';
|
||||||
import { createTransport } from 'nodemailer';
|
import { createTransport } from 'nodemailer';
|
||||||
import { render } from '@react-email/render';
|
import { render } from '@react-email/render';
|
||||||
import MagicLinkEmail from './emails/magicLinkEmail';
|
import MagicLinkEmail from './emails/magicLinkEmail';
|
||||||
import { SINGLE_TENANT_ORG_DOMAIN, SINGLE_TENANT_ORG_ID } from './lib/constants';
|
|
||||||
import bcrypt from 'bcryptjs';
|
import bcrypt from 'bcryptjs';
|
||||||
import { createAccountRequest } from './actions';
|
import { getSSOProviders } from '@/ee/sso/sso';
|
||||||
import { getSSOProviders, handleJITProvisioning } from '@/ee/sso/sso';
|
|
||||||
import { hasEntitlement } from '@/features/entitlements/server';
|
import { hasEntitlement } from '@/features/entitlements/server';
|
||||||
import { isServiceError } from './lib/utils';
|
import { onCreateUser } from '@/lib/authUtils';
|
||||||
import { ServiceErrorException } from './lib/serviceError';
|
|
||||||
import { createLogger } from "@sourcebot/logger";
|
|
||||||
|
|
||||||
export const runtime = 'nodejs';
|
export const runtime = 'nodejs';
|
||||||
|
|
||||||
|
|
@ -37,8 +33,6 @@ declare module 'next-auth/jwt' {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logger = createLogger('web-auth');
|
|
||||||
|
|
||||||
export const getProviders = () => {
|
export const getProviders = () => {
|
||||||
const providers: Provider[] = [];
|
const providers: Provider[] = [];
|
||||||
|
|
||||||
|
|
@ -134,91 +128,6 @@ export const getProviders = () => {
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCreateUser = async ({ user }: { user: AuthJsUser }) => {
|
|
||||||
// In single-tenant mode, we assign the first user to sign
|
|
||||||
// up as the owner of the default org.
|
|
||||||
if (
|
|
||||||
env.SOURCEBOT_TENANCY_MODE === 'single'
|
|
||||||
) {
|
|
||||||
const defaultOrg = await prisma.org.findUnique({
|
|
||||||
where: {
|
|
||||||
id: SINGLE_TENANT_ORG_ID,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
members: {
|
|
||||||
where: {
|
|
||||||
role: {
|
|
||||||
not: OrgRole.GUEST,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!defaultOrg) {
|
|
||||||
throw new Error("Default org not found on single tenant user creation");
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't use the getOrgMembers action here because we're not authed yet
|
|
||||||
const members = await prisma.userToOrg.findMany({
|
|
||||||
where: {
|
|
||||||
orgId: SINGLE_TENANT_ORG_ID,
|
|
||||||
role: {
|
|
||||||
not: OrgRole.GUEST,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only the first user to sign up will be an owner of the default org.
|
|
||||||
const isFirstUser = members.length === 0;
|
|
||||||
if (isFirstUser) {
|
|
||||||
await prisma.$transaction(async (tx) => {
|
|
||||||
await tx.org.update({
|
|
||||||
where: {
|
|
||||||
id: SINGLE_TENANT_ORG_ID,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
members: {
|
|
||||||
create: {
|
|
||||||
role: OrgRole.OWNER,
|
|
||||||
user: {
|
|
||||||
connect: {
|
|
||||||
id: user.id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.user.update({
|
|
||||||
where: {
|
|
||||||
id: user.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
pendingApproval: false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// TODO(auth): handle multi tenant case
|
|
||||||
if (env.AUTH_EE_ENABLE_JIT_PROVISIONING === 'true' && hasEntitlement("sso")) {
|
|
||||||
const res = await handleJITProvisioning(user.id!, SINGLE_TENANT_ORG_DOMAIN);
|
|
||||||
if (isServiceError(res)) {
|
|
||||||
logger.error(`Failed to provision user ${user.id} for org ${SINGLE_TENANT_ORG_DOMAIN}: ${res.message}`);
|
|
||||||
throw new ServiceErrorException(res);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const res = await createAccountRequest(user.id!, SINGLE_TENANT_ORG_DOMAIN);
|
|
||||||
if (isServiceError(res)) {
|
|
||||||
logger.error(`Failed to provision user ${user.id} for org ${SINGLE_TENANT_ORG_DOMAIN}: ${res.message}`);
|
|
||||||
throw new ServiceErrorException(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const { handlers, signIn, signOut, auth } = NextAuth({
|
export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||||
secret: env.AUTH_SECRET,
|
secret: env.AUTH_SECRET,
|
||||||
adapter: PrismaAdapter(prisma),
|
adapter: PrismaAdapter(prisma),
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,14 @@ import { OrgRole } from "@sourcebot/db";
|
||||||
import { getSeats, SOURCEBOT_UNLIMITED_SEATS } from "@/features/entitlements/server";
|
import { getSeats, SOURCEBOT_UNLIMITED_SEATS } from "@/features/entitlements/server";
|
||||||
import { StatusCodes } from "http-status-codes";
|
import { StatusCodes } from "http-status-codes";
|
||||||
import { ErrorCode } from "@/lib/errorCodes";
|
import { ErrorCode } from "@/lib/errorCodes";
|
||||||
|
import { OAuth2Client } from "google-auth-library";
|
||||||
import { sew } from "@/actions";
|
import { sew } from "@/actions";
|
||||||
|
import Credentials from "next-auth/providers/credentials";
|
||||||
|
import type { User as AuthJsUser } from "next-auth";
|
||||||
|
import { onCreateUser } from "@/lib/authUtils";
|
||||||
|
import { createLogger } from "@sourcebot/logger";
|
||||||
|
|
||||||
|
const logger = createLogger('web-sso');
|
||||||
|
|
||||||
export const getSSOProviders = (): Provider[] => {
|
export const getSSOProviders = (): Provider[] => {
|
||||||
const providers: Provider[] = [];
|
const providers: Provider[] = [];
|
||||||
|
|
@ -88,6 +95,82 @@ export const getSSOProviders = (): Provider[] => {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (env.AUTH_EE_GCP_IAP_ENABLED && env.AUTH_EE_GCP_IAP_AUDIENCE) {
|
||||||
|
providers.push(Credentials({
|
||||||
|
id: "gcp-iap",
|
||||||
|
name: "Google Cloud IAP",
|
||||||
|
credentials: {},
|
||||||
|
authorize: async (credentials, req) => {
|
||||||
|
try {
|
||||||
|
const iapAssertion = req.headers?.get("x-goog-iap-jwt-assertion");
|
||||||
|
if (!iapAssertion || typeof iapAssertion !== "string") {
|
||||||
|
logger.warn("No IAP assertion found in headers");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oauth2Client = new OAuth2Client();
|
||||||
|
|
||||||
|
const { pubkeys } = await oauth2Client.getIapPublicKeys();
|
||||||
|
const ticket = await oauth2Client.verifySignedJwtWithCertsAsync(
|
||||||
|
iapAssertion,
|
||||||
|
pubkeys,
|
||||||
|
env.AUTH_EE_GCP_IAP_AUDIENCE,
|
||||||
|
['https://cloud.google.com/iap']
|
||||||
|
);
|
||||||
|
|
||||||
|
const payload = ticket.getPayload();
|
||||||
|
if (!payload) {
|
||||||
|
logger.warn("Invalid IAP token payload");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const email = payload.email;
|
||||||
|
const name = payload.name || payload.email;
|
||||||
|
const image = payload.picture;
|
||||||
|
|
||||||
|
if (!email) {
|
||||||
|
logger.warn("Missing email in IAP token");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUser = await prisma.user.findUnique({
|
||||||
|
where: { email }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingUser) {
|
||||||
|
const newUser = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
email,
|
||||||
|
name,
|
||||||
|
image,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const authJsUser: AuthJsUser = {
|
||||||
|
id: newUser.id,
|
||||||
|
email: newUser.email,
|
||||||
|
name: newUser.name,
|
||||||
|
image: newUser.image,
|
||||||
|
};
|
||||||
|
|
||||||
|
await onCreateUser({ user: authJsUser });
|
||||||
|
return authJsUser;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
id: existingUser.id,
|
||||||
|
email: existingUser.email,
|
||||||
|
name: existingUser.name,
|
||||||
|
image: existingUser.image,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error("Error verifying IAP token:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,7 +212,7 @@ export const handleJITProvisioning = async (userId: string, domain: string): Pro
|
||||||
});
|
});
|
||||||
|
|
||||||
if (userToOrg) {
|
if (userToOrg) {
|
||||||
console.warn(`JIT provisioning skipped for user ${userId} since they're already a member of org ${domain}`);
|
logger.warn(`JIT provisioning skipped for user ${userId} since they're already a member of org ${domain}`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,9 @@ export const env = createEnv({
|
||||||
AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET: z.string().optional(),
|
AUTH_EE_MICROSOFT_ENTRA_ID_CLIENT_SECRET: z.string().optional(),
|
||||||
AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER: z.string().optional(),
|
AUTH_EE_MICROSOFT_ENTRA_ID_ISSUER: z.string().optional(),
|
||||||
|
|
||||||
|
AUTH_EE_GCP_IAP_ENABLED: booleanSchema.default('false'),
|
||||||
|
AUTH_EE_GCP_IAP_AUDIENCE: z.string().optional(),
|
||||||
|
|
||||||
DATA_CACHE_DIR: z.string(),
|
DATA_CACHE_DIR: z.string(),
|
||||||
|
|
||||||
// Email
|
// Email
|
||||||
|
|
|
||||||
88
packages/web/src/lib/authUtils.ts
Normal file
88
packages/web/src/lib/authUtils.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
import type { User as AuthJsUser } from "next-auth";
|
||||||
|
import { env } from "@/env.mjs";
|
||||||
|
import { prisma } from "@/prisma";
|
||||||
|
import { OrgRole } from "@sourcebot/db";
|
||||||
|
import { SINGLE_TENANT_ORG_DOMAIN, SINGLE_TENANT_ORG_ID } from "@/lib/constants";
|
||||||
|
import { hasEntitlement } from "@/features/entitlements/server";
|
||||||
|
import { isServiceError } from "@/lib/utils";
|
||||||
|
import { ServiceErrorException } from "@/lib/serviceError";
|
||||||
|
import { createAccountRequest } from "@/actions";
|
||||||
|
import { handleJITProvisioning } from "@/ee/sso/sso";
|
||||||
|
import { createLogger } from "@sourcebot/logger";
|
||||||
|
|
||||||
|
const logger = createLogger('web-auth-utils');
|
||||||
|
|
||||||
|
export const onCreateUser = async ({ user }: { user: AuthJsUser }) => {
|
||||||
|
// In single-tenant mode, we assign the first user to sign
|
||||||
|
// up as the owner of the default org.
|
||||||
|
if (
|
||||||
|
env.SOURCEBOT_TENANCY_MODE === 'single'
|
||||||
|
) {
|
||||||
|
const defaultOrg = await prisma.org.findUnique({
|
||||||
|
where: {
|
||||||
|
id: SINGLE_TENANT_ORG_ID,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
members: {
|
||||||
|
where: {
|
||||||
|
role: {
|
||||||
|
not: OrgRole.GUEST,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!defaultOrg) {
|
||||||
|
throw new Error("Default org not found on single tenant user creation");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only the first user to sign up will be an owner of the default org.
|
||||||
|
const isFirstUser = defaultOrg.members.length === 0;
|
||||||
|
if (isFirstUser) {
|
||||||
|
await prisma.$transaction(async (tx) => {
|
||||||
|
await tx.org.update({
|
||||||
|
where: {
|
||||||
|
id: SINGLE_TENANT_ORG_ID,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
members: {
|
||||||
|
create: {
|
||||||
|
role: OrgRole.OWNER,
|
||||||
|
user: {
|
||||||
|
connect: {
|
||||||
|
id: user.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.user.update({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
pendingApproval: false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// TODO(auth): handle multi tenant case
|
||||||
|
if (env.AUTH_EE_ENABLE_JIT_PROVISIONING === 'true' && hasEntitlement("sso")) {
|
||||||
|
const res = await handleJITProvisioning(user.id!, SINGLE_TENANT_ORG_DOMAIN);
|
||||||
|
if (isServiceError(res)) {
|
||||||
|
logger.error(`Failed to provision user ${user.id} for org ${SINGLE_TENANT_ORG_DOMAIN}: ${res.message}`);
|
||||||
|
throw new ServiceErrorException(res);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const res = await createAccountRequest(user.id!, SINGLE_TENANT_ORG_DOMAIN);
|
||||||
|
if (isServiceError(res)) {
|
||||||
|
logger.error(`Failed to provision user ${user.id} for org ${SINGLE_TENANT_ORG_DOMAIN}: ${res.message}`);
|
||||||
|
throw new ServiceErrorException(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
124
yarn.lock
124
yarn.lock
|
|
@ -6002,6 +6002,7 @@ __metadata:
|
||||||
eslint-plugin-react: "npm:^7.35.0"
|
eslint-plugin-react: "npm:^7.35.0"
|
||||||
eslint-plugin-react-hooks: "npm:^4.6.2"
|
eslint-plugin-react-hooks: "npm:^4.6.2"
|
||||||
fuse.js: "npm:^7.0.0"
|
fuse.js: "npm:^7.0.0"
|
||||||
|
google-auth-library: "npm:^9.15.1"
|
||||||
graphql: "npm:^16.9.0"
|
graphql: "npm:^16.9.0"
|
||||||
http-status-codes: "npm:^2.3.0"
|
http-status-codes: "npm:^2.3.0"
|
||||||
input-otp: "npm:^1.4.2"
|
input-otp: "npm:^1.4.2"
|
||||||
|
|
@ -7480,7 +7481,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"base64-js@npm:^1.3.1":
|
"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1":
|
||||||
version: 1.5.1
|
version: 1.5.1
|
||||||
resolution: "base64-js@npm:1.5.1"
|
resolution: "base64-js@npm:1.5.1"
|
||||||
checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf
|
checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf
|
||||||
|
|
@ -7517,6 +7518,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"bignumber.js@npm:^9.0.0":
|
||||||
|
version: 9.3.0
|
||||||
|
resolution: "bignumber.js@npm:9.3.0"
|
||||||
|
checksum: 10c0/f54a79cd6fc98552ac0510c1cd9381650870ae443bdb20ba9b98e3548188d941506ac3c22a9f9c69b2cc60da9be5700e87d3f54d2825310a8b2ae999dfd6d99d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"binary-extensions@npm:^2.0.0":
|
"binary-extensions@npm:^2.0.0":
|
||||||
version: 2.3.0
|
version: 2.3.0
|
||||||
resolution: "binary-extensions@npm:2.3.0"
|
resolution: "binary-extensions@npm:2.3.0"
|
||||||
|
|
@ -7628,6 +7636,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"buffer-equal-constant-time@npm:^1.0.1":
|
||||||
|
version: 1.0.1
|
||||||
|
resolution: "buffer-equal-constant-time@npm:1.0.1"
|
||||||
|
checksum: 10c0/fb2294e64d23c573d0dd1f1e7a466c3e978fe94a4e0f8183937912ca374619773bef8e2aceb854129d2efecbbc515bbd0cc78d2734a3e3031edb0888531bbc8e
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"buffer@npm:^5.5.0":
|
"buffer@npm:^5.5.0":
|
||||||
version: 5.7.1
|
version: 5.7.1
|
||||||
resolution: "buffer@npm:5.7.1"
|
resolution: "buffer@npm:5.7.1"
|
||||||
|
|
@ -8821,6 +8836,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"ecdsa-sig-formatter@npm:1.0.11, ecdsa-sig-formatter@npm:^1.0.11":
|
||||||
|
version: 1.0.11
|
||||||
|
resolution: "ecdsa-sig-formatter@npm:1.0.11"
|
||||||
|
dependencies:
|
||||||
|
safe-buffer: "npm:^5.0.1"
|
||||||
|
checksum: 10c0/ebfbf19d4b8be938f4dd4a83b8788385da353d63307ede301a9252f9f7f88672e76f2191618fd8edfc2f24679236064176fab0b78131b161ee73daa37125408c
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"ee-first@npm:1.1.1":
|
"ee-first@npm:1.1.1":
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
resolution: "ee-first@npm:1.1.1"
|
resolution: "ee-first@npm:1.1.1"
|
||||||
|
|
@ -9857,6 +9881,13 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"extend@npm:^3.0.2":
|
||||||
|
version: 3.0.2
|
||||||
|
resolution: "extend@npm:3.0.2"
|
||||||
|
checksum: 10c0/73bf6e27406e80aa3e85b0d1c4fd987261e628064e170ca781125c0b635a3dabad5e05adbf07595ea0cf1e6c5396cacb214af933da7cbaf24fe75ff14818e8f9
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"fast-content-type-parse@npm:^2.0.0":
|
"fast-content-type-parse@npm:^2.0.0":
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
resolution: "fast-content-type-parse@npm:2.0.1"
|
resolution: "fast-content-type-parse@npm:2.0.1"
|
||||||
|
|
@ -10199,6 +10230,30 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"gaxios@npm:^6.0.0, gaxios@npm:^6.1.1":
|
||||||
|
version: 6.7.1
|
||||||
|
resolution: "gaxios@npm:6.7.1"
|
||||||
|
dependencies:
|
||||||
|
extend: "npm:^3.0.2"
|
||||||
|
https-proxy-agent: "npm:^7.0.1"
|
||||||
|
is-stream: "npm:^2.0.0"
|
||||||
|
node-fetch: "npm:^2.6.9"
|
||||||
|
uuid: "npm:^9.0.1"
|
||||||
|
checksum: 10c0/53e92088470661c5bc493a1de29d05aff58b1f0009ec5e7903f730f892c3642a93e264e61904383741ccbab1ce6e519f12a985bba91e13527678b32ee6d7d3fd
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"gcp-metadata@npm:^6.1.0":
|
||||||
|
version: 6.1.1
|
||||||
|
resolution: "gcp-metadata@npm:6.1.1"
|
||||||
|
dependencies:
|
||||||
|
gaxios: "npm:^6.1.1"
|
||||||
|
google-logging-utils: "npm:^0.0.2"
|
||||||
|
json-bigint: "npm:^1.0.0"
|
||||||
|
checksum: 10c0/71f6ad4800aa622c246ceec3955014c0c78cdcfe025971f9558b9379f4019f5e65772763428ee8c3244fa81b8631977316eaa71a823493f82e5c44d7259ffac8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"gensync@npm:^1.0.0-beta.2":
|
"gensync@npm:^1.0.0-beta.2":
|
||||||
version: 1.0.0-beta.2
|
version: 1.0.0-beta.2
|
||||||
resolution: "gensync@npm:1.0.0-beta.2"
|
resolution: "gensync@npm:1.0.0-beta.2"
|
||||||
|
|
@ -10440,6 +10495,27 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"google-auth-library@npm:^9.15.1":
|
||||||
|
version: 9.15.1
|
||||||
|
resolution: "google-auth-library@npm:9.15.1"
|
||||||
|
dependencies:
|
||||||
|
base64-js: "npm:^1.3.0"
|
||||||
|
ecdsa-sig-formatter: "npm:^1.0.11"
|
||||||
|
gaxios: "npm:^6.1.1"
|
||||||
|
gcp-metadata: "npm:^6.1.0"
|
||||||
|
gtoken: "npm:^7.0.0"
|
||||||
|
jws: "npm:^4.0.0"
|
||||||
|
checksum: 10c0/6eef36d9a9cb7decd11e920ee892579261c6390104b3b24d3e0f3889096673189fe2ed0ee43fd563710e2560de98e63ad5aa4967b91e7f4e69074a422d5f7b65
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"google-logging-utils@npm:^0.0.2":
|
||||||
|
version: 0.0.2
|
||||||
|
resolution: "google-logging-utils@npm:0.0.2"
|
||||||
|
checksum: 10c0/9a4bbd470dd101c77405e450fffca8592d1d7114f245a121288d04a957aca08c9dea2dd1a871effe71e41540d1bb0494731a0b0f6fea4358e77f06645e4268c1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"gopd@npm:^1.0.1, gopd@npm:^1.2.0":
|
"gopd@npm:^1.0.1, gopd@npm:^1.2.0":
|
||||||
version: 1.2.0
|
version: 1.2.0
|
||||||
resolution: "gopd@npm:1.2.0"
|
resolution: "gopd@npm:1.2.0"
|
||||||
|
|
@ -10483,6 +10559,16 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"gtoken@npm:^7.0.0":
|
||||||
|
version: 7.1.0
|
||||||
|
resolution: "gtoken@npm:7.1.0"
|
||||||
|
dependencies:
|
||||||
|
gaxios: "npm:^6.0.0"
|
||||||
|
jws: "npm:^4.0.0"
|
||||||
|
checksum: 10c0/0a3dcacb1a3c4578abe1ee01c7d0bf20bffe8ded3ee73fc58885d53c00f6eb43b4e1372ff179f0da3ed5cfebd5b7c6ab8ae2776f1787e90d943691b4fe57c716
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"has-bigints@npm:^1.0.2":
|
"has-bigints@npm:^1.0.2":
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
resolution: "has-bigints@npm:1.1.0"
|
resolution: "has-bigints@npm:1.1.0"
|
||||||
|
|
@ -11323,6 +11409,15 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"json-bigint@npm:^1.0.0":
|
||||||
|
version: 1.0.0
|
||||||
|
resolution: "json-bigint@npm:1.0.0"
|
||||||
|
dependencies:
|
||||||
|
bignumber.js: "npm:^9.0.0"
|
||||||
|
checksum: 10c0/e3f34e43be3284b573ea150a3890c92f06d54d8ded72894556357946aeed9877fd795f62f37fe16509af189fd314ab1104d0fd0f163746ad231b9f378f5b33f4
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"json-buffer@npm:3.0.1":
|
"json-buffer@npm:3.0.1":
|
||||||
version: 3.0.1
|
version: 3.0.1
|
||||||
resolution: "json-buffer@npm:3.0.1"
|
resolution: "json-buffer@npm:3.0.1"
|
||||||
|
|
@ -11431,6 +11526,27 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"jwa@npm:^2.0.0":
|
||||||
|
version: 2.0.1
|
||||||
|
resolution: "jwa@npm:2.0.1"
|
||||||
|
dependencies:
|
||||||
|
buffer-equal-constant-time: "npm:^1.0.1"
|
||||||
|
ecdsa-sig-formatter: "npm:1.0.11"
|
||||||
|
safe-buffer: "npm:^5.0.1"
|
||||||
|
checksum: 10c0/ab3ebc6598e10dc11419d4ed675c9ca714a387481466b10e8a6f3f65d8d9c9237e2826f2505280a739cf4cbcf511cb288eeec22b5c9c63286fc5a2e4f97e78cf
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
|
"jws@npm:^4.0.0":
|
||||||
|
version: 4.0.0
|
||||||
|
resolution: "jws@npm:4.0.0"
|
||||||
|
dependencies:
|
||||||
|
jwa: "npm:^2.0.0"
|
||||||
|
safe-buffer: "npm:^5.0.1"
|
||||||
|
checksum: 10c0/f1ca77ea5451e8dc5ee219cb7053b8a4f1254a79cb22417a2e1043c1eb8a569ae118c68f24d72a589e8a3dd1824697f47d6bd4fb4bebb93a3bdf53545e721661
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"keyv@npm:^4.5.3":
|
"keyv@npm:^4.5.3":
|
||||||
version: 4.5.4
|
version: 4.5.4
|
||||||
resolution: "keyv@npm:4.5.4"
|
resolution: "keyv@npm:4.5.4"
|
||||||
|
|
@ -12404,7 +12520,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"node-fetch@npm:^2.6.7, node-fetch@npm:^2.7.0":
|
"node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.9, node-fetch@npm:^2.7.0":
|
||||||
version: 2.7.0
|
version: 2.7.0
|
||||||
resolution: "node-fetch@npm:2.7.0"
|
resolution: "node-fetch@npm:2.7.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
@ -14359,7 +14475,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"safe-buffer@npm:5.2.1, safe-buffer@npm:~5.2.0":
|
"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0":
|
||||||
version: 5.2.1
|
version: 5.2.1
|
||||||
resolution: "safe-buffer@npm:5.2.1"
|
resolution: "safe-buffer@npm:5.2.1"
|
||||||
checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
|
checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
|
||||||
|
|
@ -16025,7 +16141,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"uuid@npm:^9.0.0":
|
"uuid@npm:^9.0.0, uuid@npm:^9.0.1":
|
||||||
version: 9.0.1
|
version: 9.0.1
|
||||||
resolution: "uuid@npm:9.0.1"
|
resolution: "uuid@npm:9.0.1"
|
||||||
bin:
|
bin:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue