Skip to content

Commit

Permalink
feat: Add GitHub and Google sign-in buttons
Browse files Browse the repository at this point in the history
- Add GitHub sign-in button to allow users to sign in with GitHub.
- Add Google sign-in button to allow users to sign in with Google.
- These buttons will authenticate users and set cookies for access and refresh tokens.
refactor: Fix import paths for actions and api-routes (#123)
✨ Add dynamic googleId reference in GoogleSignInButton

ℹ️ Use dynamic googleId reference instead of hardcoding window.google.accounts.id in useState hooks and effects to improve code readability and maintainability.
✨ Fix useState initialization in GitHubSignInButton component
✨ Add error handling for Google Sign-in process

Added comments to handle errors during Google Sign-in process for better
error management and user experience.
✨ Add Google Sign-In button and configuration interfaces

- Added new types for Google Sign-In button and Google Sign-In function.
- Updated Google Sign-In button component to use the new interfaces.
- Updated global window declaration for Google Sign-In functionality.
✨ Add type annotations for state variables in GitHubSignInButton component
  • Loading branch information
sidarth-23 committed Mar 28, 2024
1 parent d4b819d commit 31632e2
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 1 deletion.
67 changes: 67 additions & 0 deletions apps/admin/components/github-signin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";

import { useEffect, useState, FC } from "react";
import { AuthProviderButton } from "@repo/ui/components";

import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { cookieSetter } from "@/actions";
import axios from "axios";
import { GITHUB_SIGN_IN_URL } from "@/api-routes";

type Props = {};

export const GitHubSignInButton: FC<Props> = () => {
const clientId = process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID;
const handleGitHubSignIn = async (credential: string) => {
try {
if (clientId && credential) {
const response = await axios.post(GITHUB_SIGN_IN_URL, {
code: credential,
});

cookieSetter({
access: response.data.tokens.access,
refresh: response.data.tokens.refresh,
});
} else throw Error("Cant find credentials");
} catch (err: any) {
// TODO: Handle error
}
};
const searchParams = useSearchParams();
// states
const [loginCallBackURL, setLoginCallBackURL] = useState<string>();
const [gitCode, setGitCode] = useState<null | string>(null);

const code = searchParams.get("code");
useEffect(() => {
if (code && !gitCode) {
setGitCode(code);
handleGitHubSignIn(code);
}
}, [code, gitCode, handleGitHubSignIn]);

useEffect(() => {
const origin =
typeof window !== "undefined" && window.location.origin
? window.location.origin
: "";
setLoginCallBackURL(`${origin}/`);
}, []);

return (
<Link
passHref
legacyBehavior
href={`https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${`${loginCallBackURL}/home/login`}&scope=read:user,user:email`}
>
<AuthProviderButton
provider="github"
width="full"
columnSpan={2}
textSize="sm"
/>
</Link>
);
};
110 changes: 110 additions & 0 deletions apps/admin/components/google-signin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"use client";

import { FC, useEffect, useRef, useCallback, useState } from "react";
import Script from "next/script";
import axios, { AxiosError } from "axios";
import { GOOGLE_SIGN_IN_URL } from "@/api-routes";
import {
TGoogleSignInFunction,
TGoogleSignInErrorResponse,
TGoogleSignInSuccessResponse,
} from "@/types";
import { cookieSetter } from "@/actions";
import { useRouter } from "next/navigation";

export const GoogleSignInButton: FC = () => {
const router = useRouter();
const clientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;
const handleGoogleSignIn = async ({
clientId,
credential,
}: TGoogleSignInFunction) => {
if (!clientId || !credential) {
throw Error("Cant find credentials");
}
try {
const response = await axios.post<TGoogleSignInSuccessResponse>(
GOOGLE_SIGN_IN_URL,
{
auth_token: credential,
}
);
cookieSetter({
access: response.data.tokens.access,
refresh: response.data.tokens.refresh,
});
router.push("/admin/dashboard");
} catch (err) {
const error = err as AxiosError<TGoogleSignInErrorResponse>;
// TODO: Handle error
}
};

// refs
const googleSignInButton = useRef<HTMLDivElement>(null);
// states
const [gsiScriptLoaded, setGsiScriptLoaded] = useState(false);

const loadScript = useCallback(() => {
if (!googleSignInButton.current || gsiScriptLoaded) return;
console.log(process.env.GOOGLE_CLIENT_ID!);

// Assign googleId the value of window.google.accounts.id
const googleId = window?.google?.accounts.id;

if (!googleId) {
console.error("Google Sign-In library not loaded");
return;
}

googleId.initialize({
client_id: clientId,
callback: handleGoogleSignIn,
});

try {
googleId.renderButton(
googleSignInButton.current,
{
type: "standard",
theme: "outline",
size: "large",
logo_alignment: "center",
text: "signin_with",
} // customization attributes
);
} catch (err) {
// TODO: Handle error
}

googleId.prompt(); // also display the One Tap dialog

setGsiScriptLoaded(true);
}, [handleGoogleSignIn, gsiScriptLoaded, clientId]);

useEffect(() => {
const googleId = window?.google?.accounts.id;
if (googleId) {
loadScript();
}
return () => {
googleId.cancel();
};
}, [loadScript]);

return (
<>
<Script
src="https://accounts.google.com/gsi/client"
async
defer
onLoad={loadScript}
/>
<div
className="w-full rounded"
id="googleSignInButton"
ref={googleSignInButton}
/>
</>
);
};
2 changes: 2 additions & 0 deletions apps/admin/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './github-signin'
export * from './google-signin'
1 change: 1 addition & 0 deletions apps/admin/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
},
"include": [
"next-env.d.ts",
"./types",
"next.config.js",
"**/*.ts",
"**/*.tsx",
Expand Down
1 change: 1 addition & 0 deletions apps/admin/types/global/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './window'
19 changes: 19 additions & 0 deletions apps/admin/types/global/window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { RefObject } from "react";
import { GsiButtonConfiguration, TGoogleSignInFunction } from "../google-auth";

export {}

declare global {
interface Window {
google: {
accounts: {
id: {
initialize: (config: { client_id: string | undefined; callback: ({ clientId, credential, }: TGoogleSignInFunction) => Promise<void> }) => void;
renderButton: (element: HTMLDivElement, options: GsiButtonConfiguration) => void;
prompt: () => void;
cancel: () => void;
};
};
};
}
}
10 changes: 10 additions & 0 deletions apps/admin/types/google-auth/gsi-button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export interface GsiButtonConfiguration {
type: "standard" | "icon";
theme?: "outline" | "filled_blue" | "filled_black";
size?: "large" | "medium" | "small";
text?: "signin_with" | "signup_with" | "continue_with" | "signup_with";
shape?: "rectangular" | "pill" | "circle" | "square";
logo_alignment?: "left" | "center";
width?: number;
local?: string;
}
2 changes: 2 additions & 0 deletions apps/admin/types/google-auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './gsi-button'
export * from './signin-function'
4 changes: 4 additions & 0 deletions apps/admin/types/google-auth/signin-function.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface TGoogleSignInFunction {
clientId: string;
credential: string;
}
4 changes: 3 additions & 1 deletion apps/admin/types/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './authentication'
export * from './authentication'
export * from './global'
export * from './google-auth'

0 comments on commit 31632e2

Please sign in to comment.