From e70c0f5dee990d2694a17b019267d76e572383ab Mon Sep 17 00:00:00 2001
From: Siddharth
Date: Sun, 22 Oct 2023 19:20:34 +0530
Subject: [PATCH] fix: error handling, dark mode, consent bug fixes
---
.../button/primary-button-component.tsx | 6 +-
.../button/secondary-button-component.tsx | 2 +-
.../components/captcha-challenge/index.tsx | 47 ++++--
.../app/components/card/card.module.css | 6 +-
.../app/components/input-component.tsx | 32 +++-
apps/consent/app/components/logo/index.tsx | 59 +++++++-
.../app/components/main-container/index.tsx | 4 +-
.../app/components/next-themes-provider.tsx | 11 ++
.../scope-item/scope-item.module.css | 4 +-
apps/consent/app/components/select.tsx | 47 ++++++
apps/consent/app/components/separator.tsx | 6 +-
.../verification-code-form.tsx | 91 ++++++-----
apps/consent/app/consent/page.tsx | 13 +-
apps/consent/app/error-handler.ts | 25 +++
apps/consent/app/globals.css | 18 ++-
apps/consent/app/layout.tsx | 43 +++---
apps/consent/app/login/email-login-form.tsx | 90 +++++++++++
.../app/login/email-login-server-action.ts | 86 +++++++++++
apps/consent/app/login/page.tsx | 143 +-----------------
apps/consent/app/login/phone/form.tsx | 34 +++--
.../app/login/phone/phone-input-styles.css | 25 +--
.../consent/app/login/phone/server-actions.ts | 53 ++++---
apps/consent/app/login/verification/form.tsx | 3 +
.../app/login/verification/server-actions.ts | 16 +-
apps/consent/env.ts | 26 ++--
apps/consent/package.json | 1 +
apps/consent/services/galoy-auth/index.ts | 2 +
apps/dashboard/.env | 2 +-
pnpm-lock.yaml | 15 ++
29 files changed, 593 insertions(+), 317 deletions(-)
create mode 100644 apps/consent/app/components/next-themes-provider.tsx
create mode 100644 apps/consent/app/components/select.tsx
create mode 100644 apps/consent/app/error-handler.ts
create mode 100644 apps/consent/app/login/email-login-form.tsx
create mode 100644 apps/consent/app/login/email-login-server-action.ts
diff --git a/apps/consent/app/components/button/primary-button-component.tsx b/apps/consent/app/components/button/primary-button-component.tsx
index 80c95b1b7f3..7c1d4c88190 100644
--- a/apps/consent/app/components/button/primary-button-component.tsx
+++ b/apps/consent/app/components/button/primary-button-component.tsx
@@ -23,8 +23,10 @@ const PrimaryButton: React.FC = ({
disabled={loadOrDisable}
{...buttonProps}
className={`flex-1 ${
- !loadOrDisable ? "bg-orange-500" : "bg-orange-600"
- } text-white p-2 rounded-lg text-sm hover:bg-orange-600`}
+ !loadOrDisable
+ ? "bg-[var(--primaryButtonBackground)]"
+ : "bg-[var(--primaryButtonBackground)]"
+ } text-[var(--primaryButtonFont)] p-2 rounded-lg text-sm hover:bg-[var(--primaryButtonBackground)]`}
>
{loadOrDisable ? : children}
diff --git a/apps/consent/app/components/button/secondary-button-component.tsx b/apps/consent/app/components/button/secondary-button-component.tsx
index d6b5f37a908..54261621e28 100644
--- a/apps/consent/app/components/button/secondary-button-component.tsx
+++ b/apps/consent/app/components/button/secondary-button-component.tsx
@@ -20,7 +20,7 @@ const SecondaryButton: React.FC = ({
diff --git a/apps/consent/app/components/captcha-challenge/index.tsx b/apps/consent/app/components/captcha-challenge/index.tsx
index ed2956fbe7e..5cce684df26 100644
--- a/apps/consent/app/components/captcha-challenge/index.tsx
+++ b/apps/consent/app/components/captcha-challenge/index.tsx
@@ -1,8 +1,15 @@
"use client";
import { sendPhoneCode } from "@/app/login/phone/server-actions";
+import { env } from "@/env";
import { memo, useCallback, useEffect } from "react";
import { toast } from "react-toastify";
+declare global {
+ interface Window {
+ initGeetest: (options: any, callback: (captchaObj: any) => void) => void;
+ }
+}
+
const CaptchaChallengeComponent: React.FC<{
id: string;
challenge: string;
@@ -10,8 +17,21 @@ const CaptchaChallengeComponent: React.FC<{
login_challenge: string;
phone: string;
remember: string;
+ channel: string ;
};
}> = ({ id, challenge, formData }) => {
+ const sendMockData = async () => {
+ const mockCaptchaResult = {
+ geetest_challenge: "mockChallenge",
+ geetest_validate: "mockValidate",
+ geetest_seccode: "mockSecCode",
+ };
+ const res = await sendPhoneCode(mockCaptchaResult, formData);
+ if (res?.error) {
+ toast.error(res.message);
+ }
+ };
+
const captchaHandler = useCallback(
(captchaObj: any) => {
const onSuccess = async () => {
@@ -35,18 +55,21 @@ const CaptchaChallengeComponent: React.FC<{
);
useEffect(() => {
- // @ts-ignore
- window.initGeetest(
- {
- gt: id,
- challenge: challenge,
- offline: false,
- new_captcha: true,
- product: "bind",
- lang: "en",
- },
- captchaHandler
- );
+ if (env.NODE_ENV === "development") {
+ sendMockData();
+ } else {
+ window.initGeetest(
+ {
+ gt: id,
+ challenge: challenge,
+ offline: false,
+ new_captcha: true,
+ product: "bind",
+ lang: "en",
+ },
+ captchaHandler
+ );
+ }
}, [captchaHandler, id, challenge]);
return ;
diff --git a/apps/consent/app/components/card/card.module.css b/apps/consent/app/components/card/card.module.css
index 7e98888271e..bcb2de357bb 100644
--- a/apps/consent/app/components/card/card.module.css
+++ b/apps/consent/app/components/card/card.module.css
@@ -6,11 +6,9 @@
@media (min-width: 768px) {
.card {
- background-color: white;
+ background-color: var(--backgroundColor);
border-radius: 0.5rem;
- box-shadow:
- rgba(0, 0, 0, 0.144) 0px 1px 3px 0px,
- rgba(27, 31, 35, 0.144) 0px 0px 0px 1px;
+ box-shadow: var(--shadow);
width: auto;
margin: auto;
width: 25em;
diff --git a/apps/consent/app/components/input-component.tsx b/apps/consent/app/components/input-component.tsx
index 52391564224..d5b8ea132f4 100644
--- a/apps/consent/app/components/input-component.tsx
+++ b/apps/consent/app/components/input-component.tsx
@@ -1,21 +1,37 @@
-import React, { InputHTMLAttributes } from "react"
+import React, { InputHTMLAttributes } from "react";
interface InputProps extends InputHTMLAttributes {
- label?: string
- id: string
+ label?: string;
+ id: string;
}
const InputComponent: React.FC = ({ label, id, ...inputProps }) => {
return (
{label ? (
-
- )
-}
+ );
+};
-export default InputComponent
+export default InputComponent;
diff --git a/apps/consent/app/components/logo/index.tsx b/apps/consent/app/components/logo/index.tsx
index 9c860ca6af9..7b53b6023c5 100644
--- a/apps/consent/app/components/logo/index.tsx
+++ b/apps/consent/app/components/logo/index.tsx
@@ -1,11 +1,62 @@
import React from "react";
-import Image from "next/image";
const Logo: React.FC = () => {
return (
-
-
-
+
);
};
diff --git a/apps/consent/app/components/main-container/index.tsx b/apps/consent/app/components/main-container/index.tsx
index 77fc3191663..419331ec098 100644
--- a/apps/consent/app/components/main-container/index.tsx
+++ b/apps/consent/app/components/main-container/index.tsx
@@ -6,10 +6,10 @@ interface MainContentProps {
const MainContent: React.FC = ({ children }) => {
return (
-
+
{children}
- )
+ );
}
export default MainContent
diff --git a/apps/consent/app/components/next-themes-provider.tsx b/apps/consent/app/components/next-themes-provider.tsx
new file mode 100644
index 00000000000..cf140e5fde1
--- /dev/null
+++ b/apps/consent/app/components/next-themes-provider.tsx
@@ -0,0 +1,11 @@
+"use client";
+import React, { ReactNode } from "react";
+import { ThemeProvider } from "next-themes";
+
+interface Props {
+ children: ReactNode;
+}
+
+export default function Theme({ children }: Props) {
+ return {children};
+}
\ No newline at end of file
diff --git a/apps/consent/app/components/scope-item/scope-item.module.css b/apps/consent/app/components/scope-item/scope-item.module.css
index 66e3bc0922b..f355cf0550a 100644
--- a/apps/consent/app/components/scope-item/scope-item.module.css
+++ b/apps/consent/app/components/scope-item/scope-item.module.css
@@ -8,12 +8,14 @@
border-radius: 0.5rem;
margin-bottom: 0.5em;
position: relative;
+ color: var(--inputColor);
+ background-color: var(--inputBackground);
}
.custom_label {
position: relative;
cursor: pointer;
- flex-grow: 1;
+ flex-grow: 1;
}
.text-gray-700 {
diff --git a/apps/consent/app/components/select.tsx b/apps/consent/app/components/select.tsx
new file mode 100644
index 00000000000..98ec83d115b
--- /dev/null
+++ b/apps/consent/app/components/select.tsx
@@ -0,0 +1,47 @@
+import React, { SelectHTMLAttributes } from "react";
+
+interface SelectProps extends SelectHTMLAttributes {
+ label?: string;
+ id: string;
+ options: string[];
+}
+
+const SelectComponent: React.FC = ({
+ label,
+ id,
+ options,
+ ...selectProps
+}) => {
+ return (
+
+ {label ? (
+
+ {label}
+
+ ) : null}
+
+
+ );
+};
+
+export default SelectComponent;
diff --git a/apps/consent/app/components/separator.tsx b/apps/consent/app/components/separator.tsx
index acb3af0da74..c4e79e9b3aa 100644
--- a/apps/consent/app/components/separator.tsx
+++ b/apps/consent/app/components/separator.tsx
@@ -8,13 +8,13 @@ const Separator: React.FC = ({ children }) => {
return (
);
diff --git a/apps/consent/app/components/varification-components/verification-code-form.tsx b/apps/consent/app/components/varification-components/verification-code-form.tsx
index 5d1ca1d153f..fd88d643efb 100644
--- a/apps/consent/app/components/varification-components/verification-code-form.tsx
+++ b/apps/consent/app/components/varification-components/verification-code-form.tsx
@@ -1,4 +1,5 @@
-import React from "react";
+"use client";
+import React, { useState, useEffect } from "react";
import InputComponent from "@/app/components/input-component";
import PrimaryButtonComponent from "@/app/components/button/primary-button-component";
@@ -18,41 +19,59 @@ const VerificationCodeForm: React.FC = ({
remember,
loginType,
value,
-}) => (
- <>
-
- Enter Verification Code
-
-
- >
-);
+ Enter Verification Code
+
+
+ >
+ );
+};
export default VerificationCodeForm;
diff --git a/apps/consent/app/consent/page.tsx b/apps/consent/app/consent/page.tsx
index 3a92f847195..bea85cdf5f8 100644
--- a/apps/consent/app/consent/page.tsx
+++ b/apps/consent/app/consent/page.tsx
@@ -111,7 +111,7 @@ const Consent = async ({ searchParams }: { searchParams: ConsentProps }) => {
value={consent_challenge}
/>
-
+
Application{" "}
{client.client_name || client.client_id} wants
access resources on your behalf and to:
@@ -121,13 +121,10 @@ const Consent = async ({ searchParams }: { searchParams: ConsentProps }) => {
))}
-
+
Do you want to be asked next time when this application wants to
- access your data?
-
-
- The application will not be able to ask for more permissions without
- your consent.
+ access your data? The application will not be able to ask for more
+ permissions without your consent.
{client.policy_uri && (
@@ -159,7 +156,7 @@ const Consent = async ({ searchParams }: { searchParams: ConsentProps }) => {
value="1"
className="mr-2"
/>
-
+
Do not ask me again
diff --git a/apps/consent/app/error-handler.ts b/apps/consent/app/error-handler.ts
new file mode 100644
index 00000000000..1ff3526bc58
--- /dev/null
+++ b/apps/consent/app/error-handler.ts
@@ -0,0 +1,25 @@
+import axios from "axios";
+
+interface ErrorResponse {
+ error: boolean;
+ message: string;
+ responsePayload: null;
+}
+
+export const handleAxiosError = (err: any): ErrorResponse => {
+ if (axios.isAxiosError(err) && err.response) {
+ console.error("Error:", err.response.data.error);
+ return {
+ error: true,
+ message: err.response?.data?.error?.name || err?.response?.data?.error,
+ responsePayload: null,
+ };
+ }
+
+ console.error("An unknown error occurred", err);
+ return {
+ error: true,
+ message: "An unknown error occurred",
+ responsePayload: null,
+ };
+};
diff --git a/apps/consent/app/globals.css b/apps/consent/app/globals.css
index 3c1a8130bb6..182e238a47d 100644
--- a/apps/consent/app/globals.css
+++ b/apps/consent/app/globals.css
@@ -2,7 +2,6 @@
@tailwind components;
@tailwind utilities;
-
:root {
--transparent: rgba(0, 0, 0, 0);
--white: #ffffff;
@@ -31,26 +30,33 @@
--error: #dc2626;
--error9: #fee2e2;
--warning: #f59e0b;
-
- /* default */
}
[data-theme="light"] {
+ --backgroundColor: var(--white);
--overlayBackground: rgba(255, 255, 255, 0.746);
--backColor: var(--black);
--primaryButtonBackground: var(--orange);
--primaryButtonFont: var(--white);
--inputBackground: var(--grey5);
--inputColor: var(--black);
- background-color: var(--white);
+ --inputSecondary: var(--grey2);
+ --shadow:
+ rgba(0, 0, 0, 0.144) 0px 1px 3px 0px,
+ rgba(27, 31, 35, 0.144) 0px 0px 0px 1px;
}
[data-theme="dark"] {
+ --backgroundColor: var(--black);
--overlayBackground: rgba(16, 16, 16, 0.746);
--backColor: var(--white);
--primaryButtonBackground: var(--primary4);
--primaryButtonFont: var(--black);
--inputBackground: var(--darkGrey);
--inputColor: var(--white);
- background-color: var(--black);
-}
\ No newline at end of file
+ --inputSecondary: var(--grey4);
+ --shadow:
+ rgba(255, 255, 255, 0.144) 0px 1px 3px 0px,
+ rgba(227, 231, 235, 0.144) 0px 0px 0px 1px;
+}
+
diff --git a/apps/consent/app/layout.tsx b/apps/consent/app/layout.tsx
index 6b5b11c09ab..5c9e681ae09 100644
--- a/apps/consent/app/layout.tsx
+++ b/apps/consent/app/layout.tsx
@@ -4,11 +4,12 @@ import Script from "next/script";
import type { Metadata } from "next";
import { Inter_Tight } from "next/font/google";
import { ToastContainer } from "react-toastify";
+import Theme from "./components/next-themes-provider";
const inter = Inter_Tight({ subsets: ["latin"] });
export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
+ title: "Blink Consent",
+ description: "OAuth2 Client For Blink.",
};
export default function RootLayout({
@@ -17,25 +18,23 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
- <>
-
-
-
-
- {children}
-
-
- >
+
+
+
+
+ {children}
+
+
);
}
diff --git a/apps/consent/app/login/email-login-form.tsx b/apps/consent/app/login/email-login-form.tsx
new file mode 100644
index 00000000000..28d4df2c5bb
--- /dev/null
+++ b/apps/consent/app/login/email-login-form.tsx
@@ -0,0 +1,90 @@
+"use client";
+import React from "react";
+// @ts-ignore-next-line no-implicit-any error
+import { experimental_useFormState as useFormState } from "react-dom";
+import InputComponent from "../components/input-component";
+import Link from "next/link";
+import FormComponent from "../components/form-component";
+import Separator from "../components/separator";
+import PrimaryButton from "../components/button/primary-button-component";
+import SecondaryButton from "../components/button/secondary-button-component";
+import { SubmitValue } from "../index.types";
+import { submitForm } from "./email-login-server-action";
+import { toast } from "react-toastify";
+// this page is for login via email
+interface LoginProps {
+ login_challenge: string;
+}
+
+const EmailLoginForm = ({ login_challenge }: LoginProps) => {
+ const [state, formAction] = useFormState(submitForm, {
+ error: null,
+ message: null,
+ });
+
+ if (state.error) {
+ toast.error(state.message);
+ state.error = null;
+ }
+
+ return (
+
+
+
+
+
+
+ Remember me
+
+
+ or
+
+
+
+
Sign in with phone
+
+
+
+
+
+ Next
+
+
+ Cancel
+
+
+
+ );
+};
+export default EmailLoginForm;
diff --git a/apps/consent/app/login/email-login-server-action.ts b/apps/consent/app/login/email-login-server-action.ts
new file mode 100644
index 00000000000..053b77c633e
--- /dev/null
+++ b/apps/consent/app/login/email-login-server-action.ts
@@ -0,0 +1,86 @@
+"use server";
+import { redirect } from "next/navigation";
+import { hydraClient } from "../../services/hydra";
+import { cookies } from "next/headers";
+import authApi from "@/services/galoy-auth";
+import { LoginType, SubmitValue } from "../index.types";
+import { LoginEmailResponse } from "./email-login.types";
+import { headers } from "next/headers";
+import { handleAxiosError } from "@/app/error-handler";
+// this page is for login via email
+
+export async function submitForm(
+ _prevState: unknown,
+ formData: FormData
+): Promise {
+ const headersList = headers();
+ const customHeaders = {
+ "x-real-ip": headersList.get("x-real-ip"),
+ "x-forwarded-for": headersList.get("x-forwarded-for"),
+ };
+
+ const login_challenge = formData.get("login_challenge");
+ const submitValue = formData.get("submit");
+ const email = formData.get("email");
+ const remember = String(formData.get("remember") === "1");
+ if (
+ !login_challenge ||
+ !submitValue ||
+ !remember ||
+ typeof login_challenge !== "string" ||
+ typeof submitValue !== "string"
+ ) {
+ throw new Error("Invalid Value");
+ }
+
+ if (submitValue === SubmitValue.denyAccess) {
+ console.log("User denied access");
+ const response = await hydraClient.rejectOAuth2LoginRequest({
+ loginChallenge: login_challenge,
+ rejectOAuth2Request: {
+ error: "access_denied",
+ error_description: "The resource owner denied the request",
+ },
+ });
+ redirect(response.data.redirect_to);
+ }
+
+ if (!email || typeof email !== "string") {
+ console.error("Invalid Values for email");
+ throw new Error("Invalid Email Value");
+ }
+
+ let emailCodeRequest;
+ try {
+ emailCodeRequest = await authApi.requestEmailCode(email, customHeaders);
+ } catch (err) {
+ console.error("Error in requestEmailCode", err);
+ return handleAxiosError(err);
+ }
+
+ // TODO: manage error on ip rate limit
+ // TODO: manage error when trying the same email too often
+ if (!emailCodeRequest) {
+ return {
+ error: true,
+ message: "Internal Server Error",
+ responsePayload: null,
+ };
+ }
+
+ cookies().set(
+ login_challenge,
+ JSON.stringify({
+ loginType: LoginType.email,
+ loginId: emailCodeRequest,
+ value: email,
+ remember,
+ }),
+ { secure: true }
+ );
+
+ let params = new URLSearchParams({
+ login_challenge,
+ });
+ redirect(`/login/verification?${params}`);
+}
diff --git a/apps/consent/app/login/page.tsx b/apps/consent/app/login/page.tsx
index 24027a4ab56..05f089d13b5 100644
--- a/apps/consent/app/login/page.tsx
+++ b/apps/consent/app/login/page.tsx
@@ -2,100 +2,17 @@ import { OAuth2LoginRequest, OAuth2RedirectTo } from "@ory/hydra-client";
import { redirect } from "next/navigation";
import React from "react";
import { hydraClient } from "../../services/hydra";
-import InputComponent from "../components/input-component";
import Card from "../components/card";
import MainContent from "../components/main-container";
import Logo from "../components/logo";
-import { cookies } from "next/headers";
-import Link from "next/link";
-import authApi from "@/services/galoy-auth";
import Heading from "../components/heading";
import SubHeading from "../components/sub-heading";
-import FormComponent from "../components/form-component";
-import Separator from "../components/separator";
-import PrimaryButton from "../components/button/primary-button-component";
-import SecondaryButton from "../components/button/secondary-button-component";
-import { LoginType, SubmitValue } from "../index.types";
-import { LoginEmailResponse } from "./email-login.types";
-import { headers } from "next/headers";
+import EmailLoginForm from "./email-login-form";
// this page is for login via email
interface LoginProps {
login_challenge: string;
}
-async function submitForm(
- formData: FormData
-): Promise {
- "use server";
-
- const headersList = headers();
- const customHeaders = {
- "x-real-ip": headersList.get("x-real-ip"),
- "x-forwarded-for": headersList.get("x-forwarded-for"),
- };
-
- const login_challenge = formData.get("login_challenge");
- const submitValue = formData.get("submit");
- const email = formData.get("email");
- const remember = String(formData.get("remember") === "1");
- if (
- !login_challenge ||
- !submitValue ||
- !remember ||
- typeof login_challenge !== "string" ||
- typeof submitValue !== "string"
- ) {
- throw new Error("Invalid Value");
- }
-
- if (submitValue === SubmitValue.denyAccess) {
- console.log("User denied access");
- const response = await hydraClient.rejectOAuth2LoginRequest({
- loginChallenge: login_challenge,
- rejectOAuth2Request: {
- error: "access_denied",
- error_description: "The resource owner denied the request",
- },
- });
- redirect(response.data.redirect_to);
- }
-
- if (!email || typeof email !== "string") {
- console.error("Invalid Values for email");
- throw new Error("Invalid Email Value");
- }
-
- let emailCodeRequest;
- try {
- emailCodeRequest = await authApi.requestEmailCode(email, customHeaders);
- } catch (err) {
- console.error("error while calling emailRequest Code", err);
- }
-
- if (!emailCodeRequest) {
- throw new Error("Request failed to get email code");
- }
-
- // TODO: manage error on ip rate limit
- // TODO: manage error when trying the same email too often
-
- cookies().set(
- login_challenge,
- JSON.stringify({
- loginType: LoginType.email,
- loginId: emailCodeRequest,
- value: email,
- remember,
- }),
- { secure: true }
- );
-
- let params = new URLSearchParams({
- login_challenge,
- });
- redirect(`/login/verification?${params}`);
-}
-
const Login = async ({ searchParams }: { searchParams: LoginProps }) => {
const { login_challenge } = searchParams;
let body: OAuth2LoginRequest;
@@ -129,63 +46,7 @@ const Login = async ({ searchParams }: { searchParams: LoginProps }) => {
Enter your Blink Account ID to sign in to this application.
-
-
-
-
-
-
- Remember me
-
-
- or
-
-
-
-
Sign in with phone
-
-
-
-
-
- Cancel
-
-
- Next
-
-
-
+
);
diff --git a/apps/consent/app/login/phone/form.tsx b/apps/consent/app/login/phone/form.tsx
index 6fa188fc905..0e2ceee8975 100644
--- a/apps/consent/app/login/phone/form.tsx
+++ b/apps/consent/app/login/phone/form.tsx
@@ -16,6 +16,7 @@ import { E164Number } from "libphonenumber-js/types";
import { SubmitValue } from "@/app/index.types";
import "react-phone-number-input/style.css";
import "./phone-input-styles.css";
+import SelectComponent from "@/app/components/select";
interface LoginFormProps {
login_challenge: string;
@@ -41,6 +42,7 @@ const LoginForm: React.FC = ({
login_challenge: null,
phone: null,
remember: null,
+ channel: null,
},
},
}
@@ -48,7 +50,7 @@ const LoginForm: React.FC = ({
if (state.error) {
toast.error(state.message);
- state.error = null
+ state.error = null;
}
const handlePhoneNumberChange = (value?: E164Number | undefined) => {
@@ -72,7 +74,7 @@ const LoginForm: React.FC = ({
Phone
@@ -88,9 +90,15 @@ const LoginForm: React.FC = ({
name="phone"
onChange={handlePhoneNumberChange}
/>
+
-
+
= ({
-
-
- Cancel
-
+
>
diff --git a/apps/consent/app/login/phone/phone-input-styles.css b/apps/consent/app/login/phone/phone-input-styles.css
index 3fc5eff08bf..eb22a4c2867 100644
--- a/apps/consent/app/login/phone/phone-input-styles.css
+++ b/apps/consent/app/login/phone/phone-input-styles.css
@@ -1,16 +1,23 @@
.PhoneInput {
- padding: 0.5em;
- border: 2px solid rgb(215, 215, 215);
- border-radius: 0.2em;
- margin-bottom: 1em;
+ padding: 0.5em;
+ border: 2px solid rgb(215, 215, 215);
+ border-radius: 0.2em;
+ margin-bottom: 1em;
+ background-color: var(--inputBackground);
+}
+
+.PhoneInput input {
+ background-color: var(--inputBackground);
}
.PhoneInput input:focus {
- outline: none;
- box-shadow: none;
- border-color: transparent;
+ outline: none;
+ box-shadow: none;
+ border-color: transparent;
+ background-color: var(--inputBackground);
}
.PhoneInput:focus-within {
- border: 2px solid rgb(30, 111, 250);
-}
\ No newline at end of file
+ border: 2px solid rgb(30, 111, 250);
+ background-color: var(--inputBackground);
+}
diff --git a/apps/consent/app/login/phone/server-actions.ts b/apps/consent/app/login/phone/server-actions.ts
index fe5f4243d09..5e54586f749 100644
--- a/apps/consent/app/login/phone/server-actions.ts
+++ b/apps/consent/app/login/phone/server-actions.ts
@@ -10,6 +10,7 @@ import {
import { LoginType, SubmitValue } from "@/app/index.types";
import { isValidPhoneNumber } from "libphonenumber-js";
import { hydraClient } from "@/services/hydra";
+import { handleAxiosError } from "@/app/error-handler";
export const getCaptchaChallenge = async (
_prevState: unknown,
@@ -24,11 +25,18 @@ export const getCaptchaChallenge = async (
const login_challenge = form.get("login_challenge");
const remember = form.get("remember") === "1";
const submitValue = form.get("submit");
+ let channel = form.get("channel");
- if (!submitValue || !login_challenge || typeof login_challenge !== "string") {
- throw new Error("submitValue not provided");
+ if (
+ !submitValue ||
+ !login_challenge ||
+ !channel ||
+ typeof channel !== "string" ||
+ typeof login_challenge !== "string"
+ ) {
+ throw new Error("Invalid Values provided");
}
-
+ channel = channel.toUpperCase();
if (submitValue === SubmitValue.denyAccess) {
console.log("User denied access");
const response = await hydraClient.rejectOAuth2LoginRequest({
@@ -62,7 +70,22 @@ export const getCaptchaChallenge = async (
};
}
- const res = await authApi.requestPhoneCaptcha(customHeaders);
+ let res;
+ try {
+ res = await authApi.requestPhoneCaptcha(customHeaders);
+ } catch (err) {
+ console.error("error in requestPhoneCaptcha", err);
+ return handleAxiosError(err);
+ }
+
+ if (!res) {
+ return {
+ error: true,
+ message: "Cannot get Captcha",
+ responsePayload: null,
+ };
+ }
+
const id = res.id;
const challenge = res.challengeCode;
return {
@@ -90,6 +113,7 @@ export const sendPhoneCode = async (
login_challenge: string;
phone: string;
remember: string;
+ channel: string ;
}
): Promise
=> {
const headersList = headers();
@@ -100,32 +124,21 @@ export const sendPhoneCode = async (
const login_challenge = formData.login_challenge;
const phone = formData.phone;
const remember = String(formData.remember) === "true";
-
+ const channel = formData.channel ?? "SMS";
let res;
+
try {
res = await authApi.requestPhoneCode(
phone,
result.geetest_challenge,
result.geetest_validate,
result.geetest_seccode,
+ channel,
customHeaders
);
} catch (err) {
- if (axios.isAxiosError(err) && err.response) {
- console.error("Error in 'phone/code' action", err.response.data);
- return {
- error: true,
- message: err.response.data.error || "An unknown error occurred",
- responsePayload: null,
- };
- } else {
- console.error("An unknown error occurred", err);
- return {
- error: true,
- message: "An unknown error occurred",
- responsePayload: null,
- };
- }
+ console.error("error in requestPhoneCode", err);
+ return handleAxiosError(err);
}
if (res?.data?.success !== true) {
diff --git a/apps/consent/app/login/verification/form.tsx b/apps/consent/app/login/verification/form.tsx
index 48647bf5b84..5b225897dd4 100644
--- a/apps/consent/app/login/verification/form.tsx
+++ b/apps/consent/app/login/verification/form.tsx
@@ -39,10 +39,13 @@ const VerificationForm: React.FC = ({
if (stateVerificationCode.error) {
toast.error(stateVerificationCode.message);
+ stateVerificationCode.message = null
}
if (stateTwoFA.error) {
toast.error(stateTwoFA.message);
+ stateTwoFA.message = null;
+
}
return (
diff --git a/apps/consent/app/login/verification/server-actions.ts b/apps/consent/app/login/verification/server-actions.ts
index 3cefef8380b..762bdff7fa5 100644
--- a/apps/consent/app/login/verification/server-actions.ts
+++ b/apps/consent/app/login/verification/server-actions.ts
@@ -1,4 +1,5 @@
"use server";
+import { handleAxiosError } from "@/app/error-handler";
import { getUserId } from "@/app/graphql/queries/me-query";
import { LoginType } from "@/app/index.types";
import authApi from "@/services/galoy-auth";
@@ -39,18 +40,7 @@ export const submitFormTotp = async (_prevState: unknown, form: FormData) => {
);
} catch (err) {
console.error("error in 'totp/validate' ", err);
- if (axios.isAxiosError(err) && err.response) {
- console.error("error in 'totp/validate' ", err.response.data.error);
- return {
- error: true,
- message: err.response.data.error,
- };
- } else {
- return {
- error: true,
- message: "unknown Error ",
- };
- }
+ return handleAxiosError(err);
}
const userId = await getUserId(authToken);
@@ -123,6 +113,7 @@ export const submitForm = async (_prevState: unknown, form: FormData) => {
userId = loginResponse.id;
} catch (err) {
console.error("error in 'phone/login' ", err);
+ return handleAxiosError(err);
}
} else if (loginType === LoginType.email) {
try {
@@ -136,6 +127,7 @@ export const submitForm = async (_prevState: unknown, form: FormData) => {
userId = loginResponse.id;
} catch (err) {
console.error("error in 'email/login' ", err);
+ return handleAxiosError(err);
}
} else {
throw new Error("Invalid Value");
diff --git a/apps/consent/env.ts b/apps/consent/env.ts
index 787137e947b..822980832f3 100644
--- a/apps/consent/env.ts
+++ b/apps/consent/env.ts
@@ -2,16 +2,18 @@ import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const env = createEnv({
- server: {
- HYDRA_ADMIN_URL: z.string().default("http://localhost:4445"),
- CORE_AUTH_URL: z.string().default("http://localhost:4002/auth"),
- },
- shared: {
- GRAPHQL_ENDPOINT: z.string().default("http://localhost:4002/graphql"),
- },
- runtimeEnv: {
- CORE_AUTH_URL: process.env.CORE_AUTH_URL,
- HYDRA_ADMIN_URL: process.env.HYDRA_ADMIN_URL,
- GRAPHQL_ENDPOINT: process.env.GRAPHQL_ENDPOINT,
- },
+ server: {
+ HYDRA_ADMIN_URL: z.string().default("http://localhost:4445"),
+ CORE_AUTH_URL: z.string().default("http://localhost:4002/auth"),
+ },
+ shared: {
+ GRAPHQL_ENDPOINT: z.string().default("http://localhost:4002/graphql"),
+ NODE_ENV: z.string().default("development"),
+ },
+ runtimeEnv: {
+ CORE_AUTH_URL: process.env.CORE_AUTH_URL,
+ HYDRA_ADMIN_URL: process.env.HYDRA_ADMIN_URL,
+ GRAPHQL_ENDPOINT: process.env.GRAPHQL_ENDPOINT,
+ NODE_ENV: process.env.NODE_ENV,
+ },
});
diff --git a/apps/consent/package.json b/apps/consent/package.json
index 175739455d4..6f8ae763b6e 100644
--- a/apps/consent/package.json
+++ b/apps/consent/package.json
@@ -23,6 +23,7 @@
"graphql": "^16.8.1",
"libphonenumber-js": "^1.10.47",
"next": "latest",
+ "next-themes": "^0.2.1",
"react": "latest",
"react-dom": "latest",
"react-phone-number-input": "^3.3.6",
diff --git a/apps/consent/services/galoy-auth/index.ts b/apps/consent/services/galoy-auth/index.ts
index 17e38f45f90..3fbb87156a9 100644
--- a/apps/consent/services/galoy-auth/index.ts
+++ b/apps/consent/services/galoy-auth/index.ts
@@ -72,6 +72,7 @@ const authApi = {
challengeCode: string,
validationCode: string,
secCode: string,
+ channel: string,
customHeaders?: object
): Promise => {
const response = await axiosInstance.post(
@@ -81,6 +82,7 @@ const authApi = {
challengeCode,
validationCode,
secCode,
+ channel,
},
customHeaders ? { headers: customHeaders } : undefined
);
diff --git a/apps/dashboard/.env b/apps/dashboard/.env
index 75ff8e8c341..5a044b62cf4 100644
--- a/apps/dashboard/.env
+++ b/apps/dashboard/.env
@@ -1,5 +1,5 @@
# REPLACE THIS IT IS FOR TESTING
-NEXTAUTH_URL=https://c890-2405-201-301c-5b67-89d6-bd56-6afb-6294.ngrok-free.app
+NEXTAUTH_URL=https://39a0-2405-201-301c-5b67-8cea-1030-d56-e0b3.ngrok-free.app
NEXTAUTH_SECRET="thisismysecret"
# 2db7666c39074da4b399e8b5116ef2c6
# 2cc1869e52ad47df848a6519b63bb4f4
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6a1f068c48d..f42930657f3 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -45,6 +45,9 @@ importers:
next:
specifier: latest
version: 13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)
+ next-themes:
+ specifier: ^0.2.1
+ version: 0.2.1(next@13.5.4)(react-dom@18.2.0)(react@18.2.0)
react:
specifier: latest
version: 18.2.0
@@ -13805,6 +13808,18 @@ packages:
uuid: 8.3.2
dev: false
+ /next-themes@0.2.1(next@13.5.4)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
+ peerDependencies:
+ next: '*'
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ next: 13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/next@13.5.4(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-+93un5S779gho8y9ASQhb/bTkQF17FNQOtXLKAj3lsNgltEcF0C5PMLLncDmH+8X1EnJH1kbqAERa29nRXqhjA==}
engines: {node: '>=16.14.0'}