Skip to content

Commit

Permalink
feat: github oauth
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Aug 5, 2024
1 parent 9dcf394 commit 0b985e0
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 40 deletions.
7 changes: 5 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
UNDB_DB_TURSO_URL=libsql://undb-project-nichenqin.turso.io
UNDB_DB_TURSO_AUTH_TOKEN=
UNDB_DB_TURSO_URL=libsql://127.0.0.1:8080?tls=0
UNDB_DB_TURSO_AUTH_TOKEN=

GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
3 changes: 2 additions & 1 deletion apps/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { swagger } from "@elysiajs/swagger"
import { trpc } from "@elysiajs/trpc"
import { executionContext } from "@undb/context/server"
import { container } from "@undb/di"
import { env } from "@undb/env"
import { Graphql } from "@undb/graphql"
import { createLogger } from "@undb/logger"
import { dbMigrate } from "@undb/persistence"
Expand Down Expand Up @@ -135,7 +136,7 @@ export const app = new Elysia()
if (!user) {
return context.redirect(`/login?redirect=${context.path}`, 301)
}
if (!user.emailVerified && user.email) {
if (env.UNDB_VERIFY_EMAIL && !user.emailVerified && user.email) {
return context.redirect(`/verify-email?redirect=${context.path}`, 301)
}
},
Expand Down
84 changes: 71 additions & 13 deletions apps/backend/src/modules/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export class Auth {
return ctx.redirect("/login")
}

if (!user.emailVerified && user.email) {
if (env.UNDB_VERIFY_EMAIL && !user.emailVerified && user.email) {
return ctx.redirect(`/verify-email`, 301)
}

Expand Down Expand Up @@ -271,17 +271,19 @@ export class Auth {
await this.spaceMemberService.createMember(userId, space.id.value, "owner")
ctx.cookie[SPACE_ID_COOKIE_NAME].set({ value: space.id.value })

const verificationCode = await this.#generateEmailVerificationCode(userId, email)
await this.mailService.send({
template: "verify-email",
data: {
username: username!,
code: verificationCode,
action_url: new URL(`/api/email-verification`, env.UNDB_BASE_URL).toString(),
},
subject: "Verify your email - undb",
to: email,
})
if (env.UNDB_VERIFY_EMAIL) {
const verificationCode = await this.#generateEmailVerificationCode(userId, email)
await this.mailService.send({
template: "verify-email",
data: {
username: username!,
code: verificationCode,
action_url: new URL(`/api/email-verification`, env.UNDB_BASE_URL).toString(),
},
subject: "Verify your email - undb",
to: email,
})
}
})
}

Expand Down Expand Up @@ -423,7 +425,7 @@ export class Auth {
)
.get("/login/github", async (ctx) => {
const state = generateState()
const url = await github.createAuthorizationURL(state)
const url = await github.createAuthorizationURL(state, { scopes: ["user:email"] })
return new Response(null, {
status: 302,
headers: {
Expand Down Expand Up @@ -483,6 +485,56 @@ export class Auth {
})
}

const emailsResponse = await fetch("https://api.github.com/user/emails", {
headers: {
Authorization: `Bearer ${tokens.accessToken}`,
},
})
const emails: GithubEmail[] = await emailsResponse.json()

const primaryEmail = emails.find((email) => email.primary) ?? null
if (!primaryEmail) {
return new Response("No primary email address", {
status: 400,
})
}
if (!primaryEmail.verified) {
return new Response("Unverified email", {
status: 400,
})
}

const existingGithubUser = await this.queryBuilder
.selectFrom("undb_user")
.selectAll()
.where("undb_user.email", "=", primaryEmail.email)
.executeTakeFirst()

if (existingGithubUser) {
const spaceId = ctx.cookie[SPACE_ID_COOKIE_NAME].value
if (!spaceId) {
await this.spaceService.setSpaceContext(setContextValue, { userId: existingGithubUser.id })
}

await this.queryBuilder
.insertInto("undb_oauth_account")
.values({
provider_id: "github",
provider_user_id: githubUserResult.id.toString(),
user_id: existingGithubUser.id,
})
.execute()

const session = await lucia.createSession(existingGithubUser.id, {})
const sessionCookie = lucia.createSessionCookie(session.id)
return new Response(null, {
status: 302,
headers: {
Location: "/",
"Set-Cookie": sessionCookie.serialize(),
},
})
}
const userId = generateIdFromEntropySize(10) // 16 characters long
await withTransaction(this.queryBuilder)(async () => {
const tx = getCurrentTransaction()
Expand Down Expand Up @@ -561,3 +613,9 @@ interface GitHubUserResult {
id: number
login: string // username
}

interface GithubEmail {
email: string
primary: boolean
verified: boolean
}
1 change: 1 addition & 0 deletions apps/frontend/src/lib/images/Google.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 1 addition & 16 deletions apps/frontend/src/lib/images/github.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 3 additions & 7 deletions apps/frontend/src/routes/(auth)/login/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import { Input } from "$lib/components/ui/input/index.js"
import { Label } from "$lib/components/ui/label/index.js"
import Logo from "$lib/images/logo.svg"
import Github from "$lib/images/github.svg"
import { createMutation } from "@tanstack/svelte-query"
import { z } from "@undb/zod"
import { defaults, superForm } from "sveltekit-superforms"
Expand All @@ -12,7 +13,6 @@
import { toast } from "svelte-sonner"
import { Button } from "$lib/components/ui/button"
import { Separator } from "$lib/components/ui/separator"
import { GithubIcon } from "lucide-svelte"
const schema = z.object({
email: z.string().email(),
Expand All @@ -30,10 +30,6 @@
toast.error(error.message)
await goto("/signup")
},
onSettled(data, error, variables, context) {
console.log(data)
console.log(error)
},
})
const form = superForm(
Expand Down Expand Up @@ -112,9 +108,9 @@
<a href="/signup" class="underline"> Sign up </a>
</div>
<Separator class="my-6" />
<div>
<div class="space-y-2">
<Button href="/login/github" variant="secondary" class="w-full">
<GithubIcon class="mr-2 h-5 w-5" />
<img class="mr-2 h-5 w-5" src={Github} alt="github" />
Login with Github
</Button>
</div>
Expand Down
9 changes: 9 additions & 0 deletions apps/frontend/src/routes/(auth)/signup/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
import { Input } from "$lib/components/ui/input/index.js"
import { Label } from "$lib/components/ui/label/index.js"
import Logo from "$lib/images/logo.svg"
import Github from "$lib/images/github.svg"
import { createMutation } from "@tanstack/svelte-query"
import { z } from "@undb/zod"
import { defaults, superForm } from "sveltekit-superforms"
import { zodClient } from "sveltekit-superforms/adapters"
import * as Form from "$lib/components/ui/form"
import { toast } from "svelte-sonner"
import { Separator } from "$lib/components/ui/separator"
const schema = z.object({
email: z.string().email(),
Expand Down Expand Up @@ -159,6 +161,13 @@
Already have an account?
<a href="/login" class="underline"> Sign in </a>
</div>
<Separator class="my-6" />
<div class="space-y-2">
<Button href="/login/github" variant="secondary" class="w-full">
<img class="mr-2 h-5 w-5" src={Github} alt="github" />
Login with Github
</Button>
</div>
</Card.Content>
</Card.Root>
</form>
Expand Down
5 changes: 4 additions & 1 deletion apps/frontend/src/routes/(auth)/verify-email/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script lang="ts">
import { goto } from "$app/navigation"
import Button from "$lib/components/ui/button/button.svelte"
import { OTPInput, OTPRoot } from "@jimmyverburgt/svelte-input-otp"
import { createMutation } from "@tanstack/svelte-query"
import { LoaderCircleIcon } from "lucide-svelte"
import Minus from "lucide-svelte/icons/minus"
import { defaults, superForm } from "sveltekit-superforms"
let otpref: any
Expand All @@ -21,6 +21,9 @@
body: JSON.stringify({ code: value }),
}),
mutationKey: ["verify-email"],
async onSuccess(data, variables, context) {
await goto("/")
},
})
</script>

Expand Down
8 changes: 8 additions & 0 deletions packages/env/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ export const env = createEnv({
UNDB_MAIL_HOST: z.string().optional(),
UNDB_MAIL_PORT: z.string().optional(),
UNDB_MAIL_DEFAULT_FROM: z.string().optional(),
UNDB_VERIFY_EMAIL: z
.string()
.optional()
.default("false")
.refine((v) => v === "true" || v === "false", {
message: "UNDB_VERIFY_EMAIL must be a boolean",
})
.transform((v) => v === "true"),

GITHUB_CLIENT_ID: z.string().optional(),
GITHUB_CLIENT_SECRET: z.string().optional(),
Expand Down

0 comments on commit 0b985e0

Please sign in to comment.