Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: saml login no email, auth design fixups #9507

Merged
merged 2 commits into from
Mar 6, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/client/components/AuthenticationDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import styled from '@emotion/styled'
import InviteDialog from './InviteDialog'

export const AUTH_DIALOG_WIDTH = 356
const AuthenticationDialog = styled(InviteDialog)({
alignItems: 'center',
paddingTop: 24,
paddingBottom: 24,
width: 356
width: AUTH_DIALOG_WIDTH
})

export default AuthenticationDialog
4 changes: 3 additions & 1 deletion packages/client/components/AuthenticationPage.tsx
Original file line number Diff line number Diff line change
@@ -5,13 +5,15 @@ import useAtmosphere from '../hooks/useAtmosphere'
import useDocumentTitle from '../hooks/useDocumentTitle'
import useRouter from '../hooks/useRouter'
import getValidRedirectParam from '../utils/getValidRedirectParam'
import {AUTH_DIALOG_WIDTH} from './AuthenticationDialog'
import GenericAuthentication, {AuthPageSlug, GotoAuthPage} from './GenericAuthentication'
import TeamInvitationWrapper from './TeamInvitationWrapper'

const CopyBlock = styled('div')({
marginBottom: 48,
width: 'calc(100vw - 16px)',
maxWidth: 500,
// must be no wider than the auth popup width to keep it looking clean
maxWidth: AUTH_DIALOG_WIDTH,
textAlign: 'center'
})

21 changes: 11 additions & 10 deletions packages/client/components/EmailPasswordAuthForm.tsx
Original file line number Diff line number Diff line change
@@ -30,6 +30,8 @@ import RaisedButton from './RaisedButton'
import StyledTip from './StyledTip'

interface Props {
// used to determine the coordinates of the auth popup
getOffsetTop?: () => number
email: string
invitationToken: string | undefined | null
// is the primary login action (not secondary to Google Oauth)
@@ -38,9 +40,6 @@ interface Props {
goToPage?: (page: AuthPageSlug, params: string) => void
}

const FieldGroup = styled('div')({
margin: '16px 0'
})
const FieldBlock = styled('div')<{isSSO?: boolean}>(({isSSO}) => ({
margin: '0 0 1.25rem',
visibility: isSSO ? 'hidden' : undefined
@@ -90,7 +89,7 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
const isInternalAuthEnabled = window.__ACTION__.AUTH_INTERNAL_ENABLED
const isSSOAuthEnabled = window.__ACTION__.AUTH_SSO_ENABLED

const {isPrimary, isSignin, invitationToken, email, goToPage} = props
const {getOffsetTop, isPrimary, isSignin, invitationToken, email, goToPage} = props
const {location} = useRouter()
const params = new URLSearchParams(location.search)
const isSSODefault = isSSOAuthEnabled && Boolean(params.get('sso'))
@@ -105,7 +104,7 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
const {fields, onChange, setDirtyField, validateField} = useForm({
email: {
getDefault: () => email,
validate: validateEmail
validate: signInWithSSOOnly ? undefined : validateEmail
},
password: {
getDefault: () => '',
@@ -150,6 +149,7 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
const domain = getSSODomainFromEmail(email)!
const validSSOURL = domain === ssoDomain && ssoURL
const isProbablySSO = isSSO || !fields.password.value || validSSOURL
const top = getOffsetTop?.() || 56
let optimisticPopup
if (isProbablySSO) {
// Safari blocks all calls to window.open that are not triggered SYNCHRONOUSLY from an event
@@ -164,7 +164,7 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
optimisticPopup = window.open(
'',
'SSO',
getOAuthPopupFeatures({width: 385, height: 550, top: 64})
getOAuthPopupFeatures({width: 385, height: 576, top})
)
}
const url = validSSOURL || (await getSSOUrl(atmosphere, email))
@@ -173,7 +173,7 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
return false
}
submitMutation()
const response = await getTokenFromSSO(url)
const response = await getTokenFromSSO(url, top)
if ('error' in response) {
onError(new Error(response.error || 'Error logging in'))
return true
@@ -198,6 +198,7 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
const onSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (submitting) return
onCompleted()
setDirtyField()
const {email: emailRes, password: passwordRes} = validateField()
if (emailRes.error) return
@@ -244,8 +245,8 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
<Form onSubmit={onSubmit}>
{error && <ErrorAlert message={error.message} />}
{isSSO && submitting && <HelpMessage>Continue through the login popup</HelpMessage>}
<FieldGroup>
<FieldBlock>
<div className={signInWithSSOOnly ? 'hidden' : 'mt-4 mb-4'}>
<FieldBlock isSSO={signInWithSSOOnly}>
<EmailInputField
autoFocus={!hasEmail}
{...fields.email}
@@ -263,7 +264,7 @@ const EmailPasswordAuthForm = forwardRef((props: Props, ref: any) => {
/>
</FieldBlock>
)}
</FieldGroup>
</div>
<Button size='medium' disabled={false} waiting={submitting}>
{isSignin ? SIGNIN_LABEL : CREATE_ACCOUNT_BUTTON_LABEL}
{signInWithSSOOnly ? ' with SSO' : ''}
23 changes: 16 additions & 7 deletions packages/client/components/GenericAuthentication.tsx
Original file line number Diff line number Diff line change
@@ -9,14 +9,14 @@ import {
SIGNIN_LABEL,
SIGNIN_SLUG
} from '../utils/constants'
import AuthenticationDialog from './AuthenticationDialog'
import AuthPrivacyFooter from './AuthPrivacyFooter'
import AuthenticationDialog from './AuthenticationDialog'
import DialogTitle from './DialogTitle'
import EmailPasswordAuthForm from './EmailPasswordAuthForm'
import ForgotPasswordPage from './ForgotPasswordPage'
import GoogleOAuthButtonBlock from './GoogleOAuthButtonBlock'
import MicrosoftOAuthButtonBlock from './MicrosoftOAuthButtonBlock'
import HorizontalSeparator from './HorizontalSeparator/HorizontalSeparator'
import MicrosoftOAuthButtonBlock from './MicrosoftOAuthButtonBlock'
import PlainButton from './PlainButton/PlainButton'
import SubmittedForgotPasswordPage from './SubmittedForgotPasswordPage'

@@ -70,12 +70,12 @@ const GenericAuthentication = (props: Props) => {
const {location} = useRouter()
const params = new URLSearchParams(location.search)
const email = params.get('email')

const authDialogRef = useRef<HTMLDivElement>(null)
const getOffsetTop = () => authDialogRef.current?.offsetTop || 0
const isGoogleAuthEnabled = window.__ACTION__.AUTH_GOOGLE_ENABLED
const isMicrosoftAuthEnabled = window.__ACTION__.AUTH_MICROSOFT_ENABLED
const isInternalAuthEnabled = window.__ACTION__.AUTH_INTERNAL_ENABLED
const isSSOAuthEnabled = window.__ACTION__.AUTH_SSO_ENABLED

if (page === 'forgot-password') {
return <ForgotPasswordPage goToPage={goToPage} />
}
@@ -97,7 +97,7 @@ const GenericAuthentication = (props: Props) => {
goToPage('forgot-password', `?email=${emailRef.current?.email()}`)
}
return (
<AuthenticationDialog>
<AuthenticationDialog ref={authDialogRef}>
<DialogTitle>{title}</DialogTitle>
<DialogSubTitle>
<span>{actionCopy}</span>
@@ -106,10 +106,18 @@ const GenericAuthentication = (props: Props) => {
</BrandedLink>
</DialogSubTitle>
{isGoogleAuthEnabled && (
<GoogleOAuthButtonBlock isCreate={isCreate} invitationToken={invitationToken} />
<GoogleOAuthButtonBlock
isCreate={isCreate}
invitationToken={invitationToken}
getOffsetTop={getOffsetTop}
/>
)}
{isMicrosoftAuthEnabled && (
<MicrosoftOAuthButtonBlock isCreate={isCreate} invitationToken={invitationToken} />
<MicrosoftOAuthButtonBlock
isCreate={isCreate}
invitationToken={invitationToken}
getOffsetTop={getOffsetTop}
/>
)}
{(isGoogleAuthEnabled || isMicrosoftAuthEnabled) &&
(isInternalAuthEnabled || isSSOAuthEnabled) && (
@@ -121,6 +129,7 @@ const GenericAuthentication = (props: Props) => {
isSignin={!isCreate}
invitationToken={invitationToken}
ref={emailRef}
getOffsetTop={getOffsetTop}
goToPage={goToPage}
/>
)}
12 changes: 7 additions & 5 deletions packages/client/components/GoogleOAuthButtonBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import styled from '@emotion/styled'
import clsx from 'clsx'
import React from 'react'
import useAtmosphere from '../hooks/useAtmosphere'
import useMutationProps from '../hooks/useMutationProps'
import useRouter from '../hooks/useRouter'
import logo from '../styles/theme/images/graphics/google.svg'
import GoogleClientManager from '../utils/GoogleClientManager'
import RaisedButton from './RaisedButton'
import StyledError from './StyledError'
import StyledTip from './StyledTip'
import logo from '../styles/theme/images/graphics/google.svg'
import RaisedButton from './RaisedButton'
import clsx from 'clsx'

interface Props {
invitationToken?: string
isCreate?: boolean
loginHint?: string
getOffsetTop?: () => number
}

const helpText = {
@@ -30,7 +31,7 @@ const HelpMessage = styled(StyledTip)({
})

const GoogleOAuthButtonBlock = (props: Props) => {
const {invitationToken, isCreate, loginHint} = props
const {invitationToken, isCreate, loginHint, getOffsetTop} = props
const {onError, error, submitting, onCompleted, submitMutation} = useMutationProps()
const atmosphere = useAtmosphere()
const {history, location} = useRouter()
@@ -43,7 +44,8 @@ const GoogleOAuthButtonBlock = (props: Props) => {
history,
location.search,
invitationToken,
loginHint
loginHint,
getOffsetTop
)
}
return (
6 changes: 4 additions & 2 deletions packages/client/components/MicrosoftOAuthButtonBlock.tsx
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ interface Props {
invitationToken?: string
isCreate?: boolean
loginHint?: string
getOffsetTop?: () => number
}

const helpText = {
@@ -30,7 +31,7 @@ const HelpMessage = styled(StyledTip)({
})

const MicrosoftOAuthButtonBlock = (props: Props) => {
const {invitationToken, isCreate, loginHint} = props
const {invitationToken, isCreate, loginHint, getOffsetTop} = props
const {onError, error, submitting, onCompleted, submitMutation} = useMutationProps()
const atmosphere = useAtmosphere()
const {history, location} = useRouter()
@@ -43,7 +44,8 @@ const MicrosoftOAuthButtonBlock = (props: Props) => {
history,
location.search,
invitationToken,
loginHint
loginHint,
getOffsetTop
)
}
return (
Original file line number Diff line number Diff line change
@@ -2,9 +2,10 @@ import styled from '@emotion/styled'
import graphql from 'babel-plugin-relay/macro'
import React from 'react'
import {useFragment} from 'react-relay'
import useDocumentTitle from '../hooks/useDocumentTitle'
import {TeamInvitationEmailCreateAccount_verifiedInvitation$key} from '../__generated__/TeamInvitationEmailCreateAccount_verifiedInvitation.graphql'
import useDocumentTitle from '../hooks/useDocumentTitle'
import AuthPrivacyFooter from './AuthPrivacyFooter'
import {AUTH_DIALOG_WIDTH} from './AuthenticationDialog'
import DialogContent from './DialogContent'
import DialogTitle from './DialogTitle'
import EmailPasswordAuthForm from './EmailPasswordAuthForm'
@@ -18,7 +19,7 @@ interface Props {
}

const StyledDialog = styled(InviteDialog)({
maxWidth: 356
maxWidth: AUTH_DIALOG_WIDTH
})

const TeamName = styled('span')({
3 changes: 2 additions & 1 deletion packages/client/components/TeamInvitationEmailSignin.tsx
Original file line number Diff line number Diff line change
@@ -11,14 +11,15 @@ import ForgotPasswordOneClick from './ForgotPasswordOneClick'
import InvitationCenteredCopy from './InvitationCenteredCopy'
import InvitationDialogCopy from './InvitationDialogCopy'
import InviteDialog from './InviteDialog'
import {AUTH_DIALOG_WIDTH} from './AuthenticationDialog'

interface Props {
invitationToken: string
verifiedInvitation: TeamInvitationEmailSignin_verifiedInvitation$key
}

const StyledDialog = styled(InviteDialog)({
maxWidth: 356
maxWidth: AUTH_DIALOG_WIDTH
})

const TeamName = styled('span')({
Original file line number Diff line number Diff line change
@@ -16,14 +16,15 @@ import InvitationCenteredCopy from './InvitationCenteredCopy'
import InvitationDialogCopy from './InvitationDialogCopy'
import InviteDialog from './InviteDialog'
import PlainButton from './PlainButton/PlainButton'
import {AUTH_DIALOG_WIDTH} from './AuthenticationDialog'

interface Props {
invitationToken: string
verifiedInvitation: TeamInvitationGoogleCreateAccount_verifiedInvitation$key
}

const StyledDialog = styled(InviteDialog)({
maxWidth: 356
maxWidth: AUTH_DIALOG_WIDTH
})

const StyledContent = styled(DialogContent)({
29 changes: 3 additions & 26 deletions packages/client/components/TeamInvitationWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,17 @@
import styled from '@emotion/styled'
import React, {ReactNode} from 'react'
import {PALETTE} from '../styles/paletteV3'
import Header from './AuthPage/Header'

const PageContainer = styled('div')({
alignItems: 'center',
backgroundColor: PALETTE.SLATE_200,
color: PALETTE.SLATE_700,
display: 'flex',
flexDirection: 'column',
maxWidth: '100%',
minHeight: '100vh'
})

const CenteredBlock = styled('div')({
alignItems: 'center',
display: 'flex',
flexDirection: 'column',
flex: 1,
justifyContent: 'center',
maxWidth: '100%',
padding: '2rem 1rem',
width: '100%'
})

interface Props {
children: ReactNode
}

function TeamInvitationWrapper(props: Props) {
const {children} = props
return (
<PageContainer>
<div className='flex min-h-screen max-w-full flex-col items-center bg-slate-200 text-slate-700'>
<Header />
<CenteredBlock>{children}</CenteredBlock>
</PageContainer>
<div className='maxw-full flex w-full flex-1 flex-col items-center py-8 px-4'>{children}</div>
</div>
)
}

Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ const OrgAuthenticationMetadata = (props: Props) => {
const optimisticPopup = window.open(
'',
'SSO',
getOAuthPopupFeatures({width: 385, height: 550, top: 64})
getOAuthPopupFeatures({width: 385, height: 576, top: 64})
)

// Get the Sign-on URL, which includes metadataURL in the RelayState
9 changes: 6 additions & 3 deletions packages/client/utils/GoogleClientManager.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {RouterProps} from 'react-router'
import Atmosphere from '../Atmosphere'
import {AUTH_DIALOG_WIDTH} from '../components/AuthenticationDialog'
import {MenuMutationProps} from '../hooks/useMutationProps'
import LoginWithGoogleMutation from '../mutations/LoginWithGoogleMutation'
import {LocalStorageKey} from '../types/constEnums'
import GoogleManager from './GoogleManager'
import getAnonymousId from './getAnonymousId'
import getOAuthPopupFeatures from './getOAuthPopupFeatures'
import GoogleManager from './GoogleManager'
import makeHref from './makeHref'

class GoogleClientManager extends GoogleManager {
@@ -16,7 +17,8 @@ class GoogleClientManager extends GoogleManager {
history: RouterProps['history'],
pageParams: string,
invitationToken?: string,
loginHint?: string
loginHint?: string,
getOffsetTop?: () => number
) {
const {submitting, onError, onCompleted, submitMutation} = mutationProps
const providerState = Math.random().toString(36).substring(5)
@@ -31,10 +33,11 @@ class GoogleClientManager extends GoogleManager {
})
const uri = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`
submitMutation()
const top = getOffsetTop?.() || 56
const popup = window.open(
uri,
'OAuth',
getOAuthPopupFeatures({width: 356, height: 530, top: 56})
getOAuthPopupFeatures({width: AUTH_DIALOG_WIDTH, height: 576, top})
)
const closeCheckerId = window.setInterval(() => {
if (popup && popup.closed) {
9 changes: 6 additions & 3 deletions packages/client/utils/MicrosoftClientManager.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {RouterProps} from 'react-router'
import Atmosphere from '../Atmosphere'
import {AUTH_DIALOG_WIDTH} from '../components/AuthenticationDialog'
import {MenuMutationProps} from '../hooks/useMutationProps'
import LoginWithMicrosoftMutation from '../mutations/LoginWithMicrosoftMutation'
import {LocalStorageKey} from '../types/constEnums'
import MicrosoftManager from './MicrosoftManager'
import getAnonymousId from './getAnonymousId'
import getOAuthPopupFeatures from './getOAuthPopupFeatures'
import MicrosoftManager from './MicrosoftManager'
import makeHref from './makeHref'

class MicrosoftClientManager extends MicrosoftManager {
@@ -16,7 +17,8 @@ class MicrosoftClientManager extends MicrosoftManager {
history: RouterProps['history'],
pageParams: string,
invitationToken?: string,
loginHint?: string
loginHint?: string,
getOffsetTop?: () => number
) {
const {submitting, onError, onCompleted, submitMutation} = mutationProps
const providerState = Math.random().toString(36).substring(5)
@@ -33,10 +35,11 @@ class MicrosoftClientManager extends MicrosoftManager {
window.__ACTION__.microsoftTenantId
}/oauth2/v2.0/authorize?${params.toString()}`
submitMutation()
const top = getOffsetTop?.() || 56
const popup = window.open(
uri,
'OAuth',
getOAuthPopupFeatures({width: 356, height: 530, top: 56})
getOAuthPopupFeatures({width: AUTH_DIALOG_WIDTH, height: 576, top})
)
const closeCheckerId = window.setInterval(() => {
if (popup && popup.closed) {
7 changes: 3 additions & 4 deletions packages/client/utils/getOAuthPopupFeatures.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
interface Box {
width: number
height: number
top: number
top?: number
}

const getOAuthPopupFeatures = ({width, height, top}: Box) => {
const {outerWidth, innerWidth, outerHeight, innerHeight, screenX, screenY} = window
const startX = screenX + (outerWidth - innerWidth) / 2
const startY = screenY + (outerHeight - innerHeight) / 2
const startY = screenY + (outerHeight - innerHeight)
const left = Math.round(startX + (innerWidth - width) / 2)
// 64 is the Parabol header
const topOff = Math.round(startY + (innerHeight - height) / 2 + top)
const topOff = top ? Math.round(startY + top) : Math.round(startY + (innerHeight - height) / 2)
return `popup,width=${width},height=${height},left=${left},top=${topOff}`
}

4 changes: 2 additions & 2 deletions packages/client/utils/getTokenFromSSO.ts
Original file line number Diff line number Diff line change
@@ -12,11 +12,11 @@ type ErrorReturnType = {

type ReturnType = SucessReturnType | ErrorReturnType

const getTokenFromSSO = (url: string): ReturnType | Promise<ReturnType> => {
const getTokenFromSSO = (url: string, top?: number): ReturnType | Promise<ReturnType> => {
// It's possible we prematurely opened a popup named SSO at the URL about:blank to avoid popup blockers
// Calling window.open again will get a reference to that popup
// Then, we can update the href to the valid URL
const popup = window.open(url, 'SSO', getOAuthPopupFeatures({width: 385, height: 550, top: 64}))
const popup = window.open(url, 'SSO', getOAuthPopupFeatures({width: 385, height: 576, top}))
if (!popup) return {error: 'Failed to open login popup'}
popup.location.href = url
let closeCheckerId: undefined | number
6 changes: 3 additions & 3 deletions packages/server/graphql/private/mutations/loginSAML.ts
Original file line number Diff line number Diff line change
@@ -9,14 +9,14 @@ import {USER_PREFERRED_NAME_LIMIT} from '../../../postgres/constants'
import getKysely from '../../../postgres/getKysely'
import {getUserByEmail} from '../../../postgres/queries/getUsersByEmails'
import encodeAuthToken from '../../../utils/encodeAuthToken'
import {isSingleTenantSSO} from '../../../utils/getSAMLURLFromEmail'
import {getSSOMetadataFromURL} from '../../../utils/getSSOMetadataFromURL'
import {samlXMLValidator} from '../../../utils/samlXMLValidator'
import standardError from '../../../utils/standardError'
import bootstrapNewUser from '../../mutations/helpers/bootstrapNewUser'
import getSignOnURL from '../../public/mutations/helpers/SAMLHelpers/getSignOnURL'
import {SSORelayState} from '../../queries/SAMLIdP'
import {SSORelayState} from '../../public/queries/SAMLIdP'
import {MutationResolvers} from '../resolverTypes'
import standardError from '../../../utils/standardError'
import {isSingleTenantSSO} from '../../../utils/getSAMLURLFromEmail'

const serviceProvider = samlify.ServiceProvider({})
samlify.setSchemaValidator(samlXMLValidator)
3 changes: 2 additions & 1 deletion packages/server/graphql/public/permissions.ts
Original file line number Diff line number Diff line change
@@ -57,7 +57,8 @@ const permissionMap: PermissionMap<Resolvers> = {
},
Query: {
'*': isAuthenticated,
getDemoEntities: rateLimit({perMinute: 5, perHour: 50})
getDemoEntities: rateLimit({perMinute: 5, perHour: 50}),
SAMLIdP: rateLimit({perMinute: 120, perHour: 3600})
},
Organization: {
saml: and(isViewerBillingLeaderSource, isOrgTierSource('enterprise'))
13 changes: 13 additions & 0 deletions packages/server/graphql/public/queries/SAMLIdP.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import getSAMLURLFromEmail from '../../../utils/getSAMLURLFromEmail'
import {QueryResolvers} from '../resolverTypes'

export interface SSORelayState {
isInvited?: boolean
metadataURL?: string
}

const SAMLIdP: QueryResolvers['SAMLIdP'] = async (_source, {email, isInvited}, {dataLoader}) => {
return getSAMLURLFromEmail(email, dataLoader, isInvited)
}

export default SAMLIdP
4 changes: 2 additions & 2 deletions packages/server/graphql/public/typeDefs/Query.graphql
Original file line number Diff line number Diff line change
@@ -20,9 +20,9 @@ type Query {
): VerifiedInvitationPayload!
SAMLIdP(
"""
the email associated with a SAML login
the email associated with a SAML login. null if instance is SSO-only
"""
email: ID!
email: ID

"""
true if the user was invited, else false
33 changes: 0 additions & 33 deletions packages/server/graphql/queries/SAMLIdP.ts

This file was deleted.

4 changes: 1 addition & 3 deletions packages/server/graphql/rootQuery.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ import {GraphQLNonNull, GraphQLObjectType} from 'graphql'
import {getUserId} from '../utils/authorization'
import {GQLContext} from './graphql'
import massInvitation from './queries/massInvitation'
import SAMLIdP from './queries/SAMLIdP'
import verifiedInvitation from './queries/verifiedInvitation'
import User from './types/User'

@@ -18,7 +17,6 @@ export default new GraphQLObjectType<any, GQLContext>({
}
},
massInvitation,
verifiedInvitation,
SAMLIdP
verifiedInvitation
})
})
8 changes: 5 additions & 3 deletions packages/server/utils/getSAMLURLFromEmail.ts
Original file line number Diff line number Diff line change
@@ -19,12 +19,10 @@ const urlWithRelayState = (url: string, isInvited?: boolean | null) => {
}

const getSAMLURLFromEmail = async (
email: string,
email: string | null | undefined,
dataLoader: DataLoaderWorker,
isInvited?: boolean | null
) => {
const domainName = getSSODomainFromEmail(email)
if (!domainName) return null
if (isSingleTenantSSO) {
// For PPMI use
const pg = getKysely()
@@ -38,6 +36,10 @@ const getSAMLURLFromEmail = async (
if (!instanceURL) return null
return urlWithRelayState(instanceURL, isInvited)
}
if (!email) return null
const domainName = getSSODomainFromEmail(email)
if (!domainName) return null

const saml = await dataLoader.get('samlByDomain').load(domainName)
if (!saml) return null
const {url} = saml