From 6a100051ac73706f3ea8893ed791aa3e9d2b070c Mon Sep 17 00:00:00 2001 From: Satindar Dhillon Date: Tue, 26 Jul 2022 23:40:44 -0700 Subject: [PATCH] add mobile email sign in route to api --- .../Views/Registration/RegistrationView.swift | 4 +- .../Models/ErrorModels/LoginError.swift | 1 + .../Services/Authentication/AuthModels.swift | 2 + .../Services/Authentication/GoogleAuth.swift | 2 +- .../DataService/Errors/ServerError.swift | 4 ++ .../VerifyAuthProviderToken.swift | 2 +- .../LoginErrorMessageView.swift | 2 + .../routers/auth/mobile/mobile_auth_router.ts | 11 +++- .../api/src/routers/auth/mobile/sign_in.ts | 51 +++++++++++++++++++ 9 files changed, 74 insertions(+), 5 deletions(-) diff --git a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift index f365a467e..8bd5754ab 100644 --- a/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift +++ b/apple/OmnivoreKit/Sources/App/Views/Registration/RegistrationView.swift @@ -20,7 +20,7 @@ import Views Task { await handleAppleToken(payload: payload, authenticator: authenticator) } case let .failure(error): switch error { - case .unauthorized, .unknown: + case .unauthorized, .unknown, .pendingEmailVerification: break case .network: loginError = error @@ -34,7 +34,7 @@ import Views } catch { let submitTokenError = (error as? LoginError) ?? .unknown switch submitTokenError { - case .unauthorized, .unknown: + case .unauthorized, .unknown, .pendingEmailVerification: await handleAppleSignUp(authenticator: authenticator, payload: payload) case .network: loginError = submitTokenError diff --git a/apple/OmnivoreKit/Sources/Models/ErrorModels/LoginError.swift b/apple/OmnivoreKit/Sources/Models/ErrorModels/LoginError.swift index 8be887841..438fbb983 100644 --- a/apple/OmnivoreKit/Sources/Models/ErrorModels/LoginError.swift +++ b/apple/OmnivoreKit/Sources/Models/ErrorModels/LoginError.swift @@ -4,4 +4,5 @@ public enum LoginError: Error { case unauthorized case network case unknown + case pendingEmailVerification } diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/AuthModels.swift b/apple/OmnivoreKit/Sources/Services/Authentication/AuthModels.swift index e3149c4dd..97e165415 100644 --- a/apple/OmnivoreKit/Sources/Services/Authentication/AuthModels.swift +++ b/apple/OmnivoreKit/Sources/Services/Authentication/AuthModels.swift @@ -58,6 +58,8 @@ extension LoginError { return .unauthorized case .unknown: return .unknown + case .pendingEmailVerification: + return .pendingEmailVerification } } } diff --git a/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift b/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift index 9c3f081ab..2fa15f0f2 100644 --- a/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift +++ b/apple/OmnivoreKit/Sources/Services/Authentication/GoogleAuth.swift @@ -31,7 +31,7 @@ extension Authenticator { switch loginError { case .unauthorized, .unknown: return await createPendingUser(idToken: idToken) - case .network: + case .network, .pendingEmailVerification: return .loginError(error: .network) } } diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Errors/ServerError.swift b/apple/OmnivoreKit/Sources/Services/DataService/Errors/ServerError.swift index c3781974f..601b2e4cf 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Errors/ServerError.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Errors/ServerError.swift @@ -10,11 +10,15 @@ public enum ServerError: String, Error { case unauthenticated case timeout case unknown + case pendingEmailVerification } extension ServerError { init(serverResponse: ServerResponse) { switch serverResponse.httpUrlResponse?.statusCode { + case 418?: + self = .pendingEmailVerification + return case 401?, 403?: self = .unauthenticated return diff --git a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift index 9ed72444c..3943d5807 100644 --- a/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift +++ b/apple/OmnivoreKit/Sources/Services/DataService/Networking/ServerResources/VerifyAuthProviderToken.swift @@ -42,7 +42,7 @@ extension Networker { let urlRequest = URLRequest.create( baseURL: appEnvironment.serverBaseURL, - urlPath: "/api/mobile-auth/email-login", + urlPath: "/api/mobile-auth/email-sign-in", requestMethod: .post(params: encodedParams) ) diff --git a/apple/OmnivoreKit/Sources/Views/RegistrationViews/LoginErrorMessageView.swift b/apple/OmnivoreKit/Sources/Views/RegistrationViews/LoginErrorMessageView.swift index 4e3ccf36c..fcb06e2fb 100644 --- a/apple/OmnivoreKit/Sources/Views/RegistrationViews/LoginErrorMessageView.swift +++ b/apple/OmnivoreKit/Sources/Views/RegistrationViews/LoginErrorMessageView.swift @@ -25,6 +25,8 @@ private extension LoginError { return LocalText.networkError case .unknown: return LocalText.genericError + case .pendingEmailVerification: + return "Please check your email for a verification message." } } } diff --git a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts index 6ff44219b..eb85649dd 100644 --- a/packages/api/src/routers/auth/mobile/mobile_auth_router.ts +++ b/packages/api/src/routers/auth/mobile/mobile_auth_router.ts @@ -2,7 +2,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import express from 'express' -import { createMobileSignInResponse } from './sign_in' +import { + createMobileSignInResponse, + createMobileEmailSignInResponse, +} from './sign_in' import { createMobileSignUpResponse } from './sign_up' import { createMobileAccountCreationResponse } from './account_creation' @@ -15,6 +18,12 @@ export function mobileAuthRouter() { res.status(payload.statusCode).json(payload.json) }) + router.post('/email-sign-in', async (req, res) => { + const { email, password } = req.body + const payload = await createMobileEmailSignInResponse(email, password) + res.status(payload.statusCode).json(payload.json) + }) + router.post('/sign-up', async (req, res) => { const { token, provider, name } = req.body const payload = await createMobileSignUpResponse(token, provider, name) diff --git a/packages/api/src/routers/auth/mobile/sign_in.ts b/packages/api/src/routers/auth/mobile/sign_in.ts index c3d5112f0..8902ff482 100644 --- a/packages/api/src/routers/auth/mobile/sign_in.ts +++ b/packages/api/src/routers/auth/mobile/sign_in.ts @@ -8,6 +8,11 @@ import { } from '../auth_types' import { createMobileAuthPayload } from '../jwt_helpers' import UserModel from '../../../datalayer/user' +import { initModels } from '../../../server' +import { sendConfirmationEmail } from '../../../services/send_emails' +import { kx } from '../../../datalayer/knex_config' +import { StatusType } from '../../../datalayer/user/model' +import { comparePassword } from '../../../utils/auth' export async function createMobileSignInResponse( token?: string, @@ -31,6 +36,52 @@ export async function createMobileSignInResponse( } } +export async function createMobileEmailSignInResponse( + email?: string, + password?: string +): Promise { + try { + if (!email || !password) { + throw new Error('Missing username or password') + } + + const models = initModels(kx, false) + const user = await models.user.getWhere({ + email, + }) + + if (!user?.id || !user?.password) { + throw new Error('user not found') + } + + const validPassword = await comparePassword(password, user.password) + if (!validPassword) { + throw new Error('password is invalid') + } + + if (user.status === StatusType.Pending && user.email) { + await sendConfirmationEmail({ + id: user.id, + email: user.email, + name: user.name, + }) + return { + statusCode: 418, + json: { errorCodes: ['PENDING_VERIFICATION'] }, + } + } + + const mobileAuthPayload = await createMobileAuthPayload(user.id) + + return { + statusCode: 200, + json: mobileAuthPayload, + } + } catch (e) { + return authFailedPayload + } +} + const authFailedPayload = { statusCode: 403, json: { errorCodes: ['AUTH_FAILED'] },