diff --git a/components/dashboard/src/Login.tsx b/components/dashboard/src/Login.tsx index 44aacdfbfc5ec4..115c964b094b04 100644 --- a/components/dashboard/src/Login.tsx +++ b/components/dashboard/src/Login.tsx @@ -93,7 +93,7 @@ export function Login() { } else { errorMessage = payload.description ? payload.description : `Error: ${payload.error}`; if (payload.error === "email_taken") { - errorMessage = `Email address already exists. Log in using a different provider.`; + errorMessage = `Email address already used in another account. Please log in with ${(payload as any).host}.`; } } setErrorMessage(errorMessage); diff --git a/components/dashboard/src/projects/NewProject.tsx b/components/dashboard/src/projects/NewProject.tsx index 2ee447b42bc06a..3375b731146faa 100644 --- a/components/dashboard/src/projects/NewProject.tsx +++ b/components/dashboard/src/projects/NewProject.tsx @@ -19,6 +19,7 @@ import search from "../icons/search.svg"; import moment from "moment"; import { UserContext } from "../user-context"; import { trackEvent } from "../Analytics"; +import exclamation from "../images/exclamation.svg"; export default function NewProject() { const location = useLocation(); @@ -404,6 +405,7 @@ function GitProviders(props: { onHostSelected: (host: string, updateUser?: boolean) => void }) { const [authProviders, setAuthProviders] = useState([]); + const [ errorMessage, setErrorMessage ] = useState(undefined); useEffect(() => { (async () => { @@ -413,6 +415,8 @@ function GitProviders(props: { }, []); const selectProvider = async (ap: AuthProviderInfo) => { + setErrorMessage(undefined); + const token = await getGitpodService().server.getToken({ host: ap.host }); if (token) { props.onHostSelected(ap.host); @@ -424,8 +428,17 @@ function GitProviders(props: { onSuccess: async () => { props.onHostSelected(ap.host, true); }, - onError: (error) => { - console.log(error); + onError: (payload) => { + let errorMessage: string; + if (typeof payload === "string") { + errorMessage = payload; + } else { + errorMessage = payload.description ? payload.description : `Error: ${payload.error}`; + if (payload.error === "email_taken") { + errorMessage = `Email address already used in another account. Please log in with ${(payload as any).host}.`; + } + } + setErrorMessage(errorMessage); } }); } @@ -446,6 +459,18 @@ function GitProviders(props: { ); })} + + {errorMessage && ( +
+
+ +
+
+

{errorMessage}

+
+
+ )} + ) diff --git a/components/server/src/auth/errors.ts b/components/server/src/auth/errors.ts index 56a9f434427da7..d597ecfea8b4a4 100644 --- a/components/server/src/auth/errors.ts +++ b/components/server/src/auth/errors.ts @@ -69,12 +69,11 @@ export namespace SelectAccountException { } export interface EmailAddressAlreadyTakenException extends AuthException { - payload: string; } export namespace EmailAddressAlreadyTakenException { const type = "EmailAddressAlreadyTakenException"; - export function create(message: string) { - return AuthException.create(type, message, message); + export function create(message: string, payload: object | undefined) { + return AuthException.create(type, message, payload); } export function is(error: any): error is EmailAddressAlreadyTakenException { return AuthException.is(error) && error.authException === type; diff --git a/components/server/src/auth/generic-auth-provider.ts b/components/server/src/auth/generic-auth-provider.ts index db72d9ce56099d..95f23adb1397bc 100644 --- a/components/server/src/auth/generic-auth-provider.ts +++ b/components/server/src/auth/generic-auth-provider.ts @@ -343,7 +343,7 @@ export class GenericAuthProvider implements AuthProvider { return this.sendCompletionRedirectWithError(response, err.payload); } if (EmailAddressAlreadyTakenException.is(err)) { - return this.sendCompletionRedirectWithError(response, { error: "email_taken" }); + return this.sendCompletionRedirectWithError(response, { error: "email_taken", host: err.payload?.host }); } let message = 'Authorization failed. Please try again.'; @@ -448,15 +448,12 @@ export class GenericAuthProvider implements AuthProvider { if (!currentGitpodUser) { // signup new accounts with email adresses already taken is disallowed - const existingUserWithSameEmail = (await this.userDb.findUsersByEmail(primaryEmail))[0]; - if (existingUserWithSameEmail) { - try { - await this.userService.asserNoAccountWithEmail(primaryEmail); - } catch (error) { - log.warn(`Login attempt with matching email address.`, { ...defaultLogPayload, authUser, candidate, clientInfo }); - done(error, undefined); - return; - } + try { + await this.userService.asserNoAccountWithEmail(primaryEmail); + } catch (error) { + log.warn(`Login attempt with matching email address.`, { ...defaultLogPayload, authUser, candidate, clientInfo }); + done(error, undefined); + return; } } } diff --git a/components/server/src/user/user-service.ts b/components/server/src/user/user-service.ts index 02b21f4d86050e..60e9195414b7bf 100644 --- a/components/server/src/user/user-service.ts +++ b/components/server/src/user/user-service.ts @@ -335,8 +335,10 @@ export class UserService { /* * /!\ the given email address is used in another user account. */ + const authProviderId = existingUser.identities.find(i => i.primaryEmail === email)?.authProviderId; + const host = this.hostContextProvider.getAll().find(c => c.authProvider.authProviderId === authProviderId)?.authProvider?.info?.host || "unknown"; - throw EmailAddressAlreadyTakenException.create(`Email address is already in use.`); + throw EmailAddressAlreadyTakenException.create(`Email address is already in use.`, { host }); } } \ No newline at end of file