Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions apps/blog-next/app/api/forgot-password/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { requestPasswordReset } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { forgotPasswordForm } from '@/lib/schemas/auth'

Expand All @@ -14,15 +14,12 @@ export async function POST(request: Request) {

const { error } = await requestPasswordReset(submission.data)
if (error) {
return Response.json({
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}, {
status: error.status,
})

return Response.json(failure, { status: failure.status })
}

return Response.json(submission.success({
Expand Down
18 changes: 4 additions & 14 deletions apps/blog-next/app/api/login/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { login } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { loginForm } from '@/lib/schemas/auth'

Expand All @@ -16,20 +16,12 @@ export async function POST(request: Request) {

const { data: session, error } = await login(submission.data)
if (error) {
return Response.json({
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}, {
status: error.status,
})
}

const headers = new Headers()
for (const cookie of session.cookies) {
headers.append('set-cookie', cookie)
return Response.json(failure, { status: failure.status })
}

return Response.json(submission.success({
Expand All @@ -40,7 +32,5 @@ export async function POST(request: Request) {
? session.emailVerificationRoute ?? '/verify-email'
: '/admin',
user: session.user,
}), {
headers,
})
}))
}
8 changes: 1 addition & 7 deletions apps/blog-next/app/api/logout/route.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
import { logout, user } from '@holo-js/auth'

export async function POST() {
const signedOut = await logout()
const headers = new Headers()
for (const cookie of signedOut.cookies) {
headers.append('set-cookie', cookie)
}
await logout()

return Response.json({
ok: true,
authenticated: false,
message: 'Signed out successfully.',
user: await user(),
}, {
headers,
})
}
17 changes: 4 additions & 13 deletions apps/blog-next/app/api/register/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { loginUsing, register } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { registerForm } from '@/lib/schemas/auth'

Expand All @@ -16,23 +16,15 @@ export async function POST(request: Request) {

const { data: created, error } = await register(submission.data)
if (error) {
return Response.json({
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}, {
status: error.status,
})
}

const session = await loginUsing(created)
const headers = new Headers()
for (const cookie of session.cookies) {
headers.append('set-cookie', cookie)
return Response.json(failure, { status: failure.status })
}

const session = await loginUsing(created)
return Response.json(submission.success({
message: session.emailVerificationRequired
? 'Account created. Check your inbox to verify your email address.'
Expand All @@ -43,6 +35,5 @@ export async function POST(request: Request) {
user: session.user,
}, 201), {
status: 201,
headers,
})
}
11 changes: 4 additions & 7 deletions apps/blog-next/app/api/reset-password/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { resetPassword } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { resetPasswordForm } from '@/lib/schemas/auth'

Expand All @@ -14,15 +14,12 @@ export async function POST(request: Request) {

const { error } = await resetPassword(submission.data)
if (error) {
return Response.json({
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}, {
status: error.status,
})

return Response.json(failure, { status: failure.status })
}

return Response.json(submission.success({
Expand Down
11 changes: 4 additions & 7 deletions apps/blog-next/app/api/verify-email/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { check, verification } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { verifyEmailForm } from '@/lib/schemas/auth'

Expand All @@ -15,15 +15,12 @@ export async function POST(request: Request) {
const wasAuthenticated = await check()
const { error } = await verification.consume(submission.data.token)
if (error) {
return Response.json({
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}, {
status: error.status,
})

return Response.json(failure, { status: failure.status })
}

return Response.json(submission.success({
Expand Down
21 changes: 21 additions & 0 deletions apps/blog-next/app/auth/github/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { redirect } from 'next/navigation'
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return handleCallback(request)
}

async function handleCallback(request: Request): Promise<Response> {
const result = await callback('github', request)
if (!result.ok) {
return Response.json({
message: result.message,
}, {
status: result.status,
})
}

await auth.guard(result.guard).loginUsing(result.user)
redirect('/admin')
}
5 changes: 5 additions & 0 deletions apps/blog-next/app/auth/github/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return redirect('github', request)
}
21 changes: 21 additions & 0 deletions apps/blog-next/app/auth/google/callback/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { redirect } from 'next/navigation'
import auth from '@holo-js/auth'
import { callback } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return handleCallback(request)
}

async function handleCallback(request: Request): Promise<Response> {
const result = await callback('google', request)
if (!result.ok) {
return Response.json({
message: result.message,
}, {
status: result.status,
})
}

await auth.guard(result.guard).loginUsing(result.user)
redirect('/admin')
}
5 changes: 5 additions & 0 deletions apps/blog-next/app/auth/google/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { redirect } from '@holo-js/auth-social'

export function GET(request: Request): Promise<Response> {
return redirect('google', request)
}
5 changes: 5 additions & 0 deletions apps/blog-next/app/login/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ export default function LoginPage() {
<p style={{ margin: 0, color: '#94a3b8' }}>Use your email address and password to access the admin area.</p>
</div>

<div style={{ display: 'grid', gap: '0.65rem' }}>
<a href="/auth/google" style={{ color: '#e5e7eb', textDecoration: 'none' }}>Continue with Google</a>
<a href="/auth/github" style={{ color: '#e5e7eb', textDecoration: 'none' }}>Continue with GitHub</a>
</div>

<form onSubmit={(event) => { event.preventDefault(); form.submit() }} style={{ display: 'grid', gap: '0.9rem' }}>
<label style={{ display: 'grid', gap: '0.35rem' }}>
<span>Email</span>
Expand Down
10 changes: 5 additions & 5 deletions apps/blog-next/lib/schemas/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { field, schema } from '@holo-js/forms/schema'

export const loginForm = schema({
email: field.string().required('Email is required.').email('Enter a valid email address.'),
password: field.string().required('Password is required.').min(8, 'Password must be at least 8 characters.'),
password: field.password().required('Password is required.').min(8, 'Password must be at least 8 characters.'),
remember: field.boolean().default(false),
})

export const registerForm = schema({
name: field.string().required('Name is required.').min(3, 'Name must be at least 3 characters.'),
email: field.string().required('Email is required.').email('Enter a valid email address.'),
password: field.string().required('Password is required.').min(8, 'Password must be at least 8 characters.').confirmed(),
passwordConfirmation: field.string().required('Please confirm your password.'),
password: field.password().required('Password is required.').min(8, 'Password must be at least 8 characters.').confirmed(),
passwordConfirmation: field.password().required('Please confirm your password.'),
})

export const forgotPasswordForm = schema({
Expand All @@ -19,8 +19,8 @@ export const forgotPasswordForm = schema({

export const resetPasswordForm = schema({
token: field.string().required('Reset token is required.'),
password: field.string().required('Password is required.').min(8, 'Password must be at least 8 characters.').confirmed(),
passwordConfirmation: field.string().required('Please confirm your password.'),
password: field.password().required('Password is required.').min(8, 'Password must be at least 8 characters.').confirmed(),
passwordConfirmation: field.password().required('Please confirm your password.'),
})

export const verifyEmailForm = schema({
Expand Down
7 changes: 7 additions & 0 deletions apps/blog-nuxt/app/pages/login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ const form = useForm(loginForm, {
<p>Use your email address and password to access the admin area.</p>
</div>

<div class="social-links">
<a href="/auth/google">Continue with Google</a>
<a href="/auth/github">Continue with GitHub</a>
</div>

<form class="stack" @submit.prevent="form.submit()">
<label class="field">
<span>Email</span>
Expand Down Expand Up @@ -72,6 +77,8 @@ const form = useForm(loginForm, {
.copy p { margin: 0; color: #94a3b8; }
.stack, .field { display: grid; gap: 0.35rem; }
.stack { gap: 0.9rem; }
.social-links { display: grid; gap: 0.65rem; }
.social-links a { color: #e5e7eb; text-decoration: none; }
.remember, .links { display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap; }
.error { color: #fca5a5; }
.success { color: #86efac; }
Expand Down
13 changes: 6 additions & 7 deletions apps/blog-nuxt/server/api/forgot-password.post.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { requestPasswordReset } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { forgotPasswordForm } from '#shared/schemas/auth'

Expand All @@ -14,14 +14,13 @@ export default defineEventHandler(async (event) => {

const { error } = await requestPasswordReset(submission.data)
if (error) {
setResponseStatus(event, error.status)
return {
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}
})

setResponseStatus(event, failure.status)
return failure
}

return submission.success({
Expand Down
15 changes: 6 additions & 9 deletions apps/blog-nuxt/server/api/login.post.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { login } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { loginForm } from '#shared/schemas/auth'

Expand All @@ -16,17 +16,14 @@ export default defineEventHandler(async (event) => {

const { data: session, error } = await login(submission.data)
if (error) {
setResponseStatus(event, error.status)
return {
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}
}
})

event.node.res.setHeader('set-cookie', [...session.cookies])
setResponseStatus(event, failure.status)
return failure
}

return submission.success({
message: session.emailVerificationRequired
Expand Down
5 changes: 2 additions & 3 deletions apps/blog-nuxt/server/api/logout.post.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { logout, user } from '@holo-js/auth'

export default defineEventHandler(async (event) => {
const signedOut = await logout()
event.node.res.setHeader('set-cookie', [...signedOut.cookies])
export default defineEventHandler(async () => {
await logout()

return {
ok: true,
Expand Down
14 changes: 6 additions & 8 deletions apps/blog-nuxt/server/api/register.post.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { loginUsing, register } from '@holo-js/auth'
import { sanitizeFlashedInput, validate } from '@holo-js/forms'
import { validate } from '@holo-js/forms'

import { registerForm } from '#shared/schemas/auth'

Expand All @@ -16,18 +16,16 @@ export default defineEventHandler(async (event) => {

const { data: created, error } = await register(submission.data)
if (error) {
setResponseStatus(event, error.status)
return {
ok: false as const,
const failure = submission.fail({
status: error.status,
valid: false as const,
values: sanitizeFlashedInput(submission.values),
errors: error.fields,
}
})

setResponseStatus(event, failure.status)
return failure
}

const session = await loginUsing(created)
event.node.res.setHeader('set-cookie', [...session.cookies])
setResponseStatus(event, 201)
return submission.success({
message: session.emailVerificationRequired
Expand Down
Loading