-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
346 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { Field, Int, ObjectType } from "type-graphql"; | ||
import { | ||
Entity, | ||
PrimaryGeneratedColumn, | ||
Column, | ||
CreateDateColumn, | ||
UpdateDateColumn, | ||
BaseEntity, | ||
ManyToOne, | ||
} from "typeorm"; | ||
import { User } from "./User"; | ||
|
||
@ObjectType() | ||
@Entity() | ||
export class Account extends BaseEntity { | ||
@Field(() => Int) | ||
@PrimaryGeneratedColumn() | ||
id!: number; | ||
|
||
@Field() | ||
@Column() | ||
userId!: number; | ||
|
||
@ManyToOne(() => User, (user) => user.addresses) | ||
user!: User; | ||
|
||
@Field(() => String) | ||
@Column({ | ||
type: "enum", | ||
enum: ["OAUTH", "PASSWORD"], | ||
}) | ||
type!: "OAUTH" | "PASSWORD"; | ||
|
||
@Field() | ||
@Column() | ||
provider!: string; | ||
|
||
@Field() | ||
@Column() | ||
providerAccountId!: string; | ||
|
||
@Field({ nullable: true }) | ||
@Column({ nullable: true }) | ||
refresh_token!: string; | ||
|
||
@Field({ nullable: true }) | ||
@Column({ nullable: true }) | ||
access_token!: string; | ||
|
||
@Field({ nullable: true }) | ||
@Column({ nullable: true }) | ||
expires_at!: number; | ||
|
||
@Field({ nullable: true }) | ||
@Column({ nullable: true }) | ||
token_type!: string; | ||
|
||
@Field({ nullable: true }) | ||
@Column({ nullable: true }) | ||
scope!: string; | ||
|
||
@Field({ nullable: true }) | ||
@Column({ nullable: true }) | ||
id_token!: string; | ||
|
||
@Field({ nullable: true }) | ||
@Column({ nullable: true }) | ||
session_state!: string; | ||
|
||
@Field(() => String) | ||
@CreateDateColumn() | ||
created_at = new Date(); | ||
|
||
@Field(() => String) | ||
@UpdateDateColumn() | ||
updated_at = new Date(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { Router } from "express"; | ||
import jwt from "jsonwebtoken"; | ||
import { Account } from "../entities/Account"; | ||
import { User } from "../entities/User"; | ||
import { MyContext } from "../types"; | ||
import { getGoogleOAuthToken } from "../utils/oauth"; | ||
|
||
const router: Router = Router(); | ||
|
||
interface GoogleUserResponse { | ||
iss: string; | ||
azp: string; | ||
aud: string; | ||
sub: string; | ||
email: string; | ||
email_verified: boolean; | ||
at_hash: string; | ||
name: string; | ||
picture: string; | ||
given_name: string; | ||
family_name: string; | ||
locale: string; | ||
iat: number; | ||
exp: number; | ||
} | ||
|
||
router.get( | ||
"/google/callback", | ||
async (req: MyContext["req"], res: MyContext["res"]) => { | ||
// 1. Get the code from query string | ||
|
||
const code = req.query.code as string; | ||
|
||
// 2. Get the id and access token | ||
|
||
const { id_token, access_token, refresh_token, expires_in } = | ||
await getGoogleOAuthToken({ code }); | ||
|
||
// 3. Get user with token | ||
|
||
const profile = jwt.decode(id_token) as GoogleUserResponse; | ||
|
||
// 4. Upsert the User | ||
|
||
const exisitingUser = await Account.findOne({ | ||
where: { | ||
providerAccountId: profile.sub, | ||
}, | ||
relations: { | ||
user: true, | ||
}, | ||
}); | ||
|
||
if (exisitingUser) { | ||
req.session.userId = exisitingUser.userId; | ||
res.redirect(`${process.env.CLIENT_URL}`); | ||
return; | ||
} | ||
|
||
const existingUserByEmail = await User.findOne({ | ||
where: { | ||
email: profile.email, | ||
}, | ||
}); | ||
|
||
if (existingUserByEmail) { | ||
await Account.create({ | ||
userId: existingUserByEmail.id, | ||
type: "OAUTH", | ||
provider: "google", | ||
providerAccountId: profile.sub, | ||
access_token: access_token, | ||
refresh_token: refresh_token, | ||
expires_at: expires_in, | ||
}).save(); | ||
|
||
req.session.userId = existingUserByEmail.id; | ||
res.redirect(`${process.env.CLIENT_URL}`); | ||
return; | ||
} | ||
|
||
const userRes = await User.create({ | ||
password: "unset", | ||
first_name: profile.given_name, | ||
last_name: profile.family_name, | ||
email: profile.email, | ||
email_verified: profile.email_verified, | ||
imageUrl: | ||
profile.picture ?? | ||
`https://api.dicebear.com/6.x/micah/svg?size=256&seed=${profile.name}`, | ||
}).save(); | ||
|
||
await Account.create({ | ||
userId: userRes.id, | ||
type: "OAUTH", | ||
provider: "google", | ||
providerAccountId: profile.sub, | ||
access_token: access_token, | ||
refresh_token: refresh_token, | ||
expires_at: expires_in, | ||
}).save(); | ||
|
||
req.session.userId = userRes.id; | ||
res.redirect(`${process.env.CLIENT_URL}`); | ||
} | ||
); | ||
|
||
export default router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { GOOGLE_OAUTH_REDIRECT_URL } from "../constants"; | ||
|
||
interface GoogleTokenResult { | ||
access_token: string; | ||
expires_in: number; | ||
refresh_token: string; | ||
scope: string; | ||
id_token: string; | ||
} | ||
|
||
export const getGoogleOAuthToken = async ({ | ||
code, | ||
}: { | ||
code: string; | ||
}): Promise<GoogleTokenResult> => { | ||
const url = "https://oauth2.googleapis.com/token"; | ||
const values = new URLSearchParams({ | ||
code, | ||
client_id: process.env.GOOGLE_CLIENT_ID, | ||
client_secret: process.env.GOOGLE_CLIENT_SECRET, | ||
redirect_uri: GOOGLE_OAUTH_REDIRECT_URL, | ||
grant_type: "authorization_code", | ||
}); | ||
try { | ||
const res = await fetch(url, { | ||
method: "POST", | ||
body: values, | ||
headers: { | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
}, | ||
}); | ||
|
||
return res.json(); | ||
} catch (error: any) { | ||
console.error(error); | ||
throw new Error(error.message); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
NEXT_PUBLIC_API_URL= | ||
NEXT_PUBLIC_GOOGLE_CLIENT_ID= | ||
KHALTI_SECRET_KEY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,31 @@ | ||
import { Stack, Button, Box } from "@chakra-ui/react"; | ||
import { FcGoogle } from "react-icons/fc"; | ||
import { GOOGLE_OAUTH_REDIRECT_URL } from "../../../constants"; | ||
|
||
const SignInWithGoogle = () => ( | ||
<Stack spacing="4"> | ||
<Stack spacing="4" as="a" href={getGoogleOAuthUrl()}> | ||
<Button leftIcon={<Box as={FcGoogle} color="red.500" />}> | ||
Sign up with Google | ||
</Button> | ||
</Stack> | ||
); | ||
|
||
export default SignInWithGoogle; | ||
|
||
export const getGoogleOAuthUrl = () => { | ||
const rootUrl = "https://accounts.google.com/o/oauth2/v2/auth"; | ||
|
||
const qs = new URLSearchParams({ | ||
redirect_uri: GOOGLE_OAUTH_REDIRECT_URL, | ||
client_id: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID, | ||
access_type: "offline", | ||
response_type: "code", | ||
prompt: "consent", | ||
scope: [ | ||
"https://www.googleapis.com/auth/userinfo.profile", | ||
"https://www.googleapis.com/auth/userinfo.email", | ||
].join(" "), | ||
}); | ||
|
||
return `${rootUrl}?${qs.toString()}`; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
c88cda8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
ecommerce-admin-client – ./apps/admin
ecommerce-admin-client.vercel.app
ecommerce-admin-client-git-main-sahrohit.vercel.app
ecommerce-admin-client-sahrohit.vercel.app
c88cda8
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
ecommerce-storefront-client – ./apps/storefront
rudejellyfish.live
ecommerce-storefront-client-sahrohit.vercel.app
ecommerce-storefront-client-git-main-sahrohit.vercel.app
www.rudejellyfish.live
hamropasal.vercel.app