-
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.
Merge pull request #32 from NIAEFEUP/feature/login
feature: login
- Loading branch information
Showing
82 changed files
with
5,362 additions
and
3,492 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,10 +15,32 @@ SESSION_DRIVER=cookie | |
|
||
FROM_EMAIL=[email protected] | ||
REPLY_TO_EMAIL=[email protected] | ||
SMTP_HOST=localhost | ||
SMTP_PORT=1025 | ||
|
||
# Rate limiting | ||
LIMITER_STORE=memory | ||
|
||
# Redis | ||
REDIS_HOST=127.0.0.1 | ||
REDIS_PORT=6379 | ||
REDIS_PASSWORD= | ||
|
||
# Ally | ||
GITHUB_CLIENT_ID=******** | ||
GITHUB_CLIENT_SECRET=******** | ||
GOOGLE_CLIENT_ID=******** | ||
GOOGLE_CLIENT_SECRET=******** | ||
LINKEDIN_CLIENT_ID=******** | ||
LINKEDIN_CLIENT_SECRET=******** | ||
|
||
# Feature flags | ||
FEATURES_DISABLE_AUTH=false | ||
|
||
# Frontend | ||
INERTIA_PUBLIC_TZ=Europe/Lisbon | ||
INERTIA_PUBLIC_EVENT_COUNTDOWN_DATE=2025-04-11 | ||
|
||
# Tuyau | ||
INERTIA_PUBLIC_APP_URL=http://127.0.0.1:3333 |
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,117 @@ | ||
import Account from '#models/account' | ||
import type { HttpContext } from '@adonisjs/core/http' | ||
import { | ||
registerWithCredentialsValidator, | ||
emailVerificationCallbackValidator, | ||
loginWithCredentialsValidator, | ||
} from '#validators/authentication' | ||
import { UserService } from '#services/user_service' | ||
import { inject } from '@adonisjs/core' | ||
import UserCreated from '#events/user_created' | ||
import SendVerificationEmail from '#listeners/send_verification_email' | ||
import { errors } from '@adonisjs/auth' | ||
|
||
@inject() | ||
export default class AuthenticationController { | ||
constructor(private userService: UserService) {} | ||
|
||
async login({ request, auth, session, response }: HttpContext) { | ||
const { email, password } = await request.validateUsing(loginWithCredentialsValidator) | ||
|
||
try { | ||
const account = await Account.verifyCredentials(`credentials:${email}`, password) | ||
|
||
await account.load('user') | ||
await auth.use('web').login(account.user) | ||
|
||
if (!account.user.isEmailVerified()) | ||
return response.redirect().toRoute('pages:auth.verify') | ||
|
||
return response.redirect().toRoute('pages:home') | ||
|
||
} catch (error) { | ||
if (error instanceof errors.E_INVALID_CREDENTIALS) { | ||
session.flashErrors({ password: 'As credenciais que introduziste não são válidas' }) | ||
return response.redirect().back() | ||
} | ||
|
||
throw error | ||
} | ||
} | ||
|
||
async logout({ auth, response }: HttpContext) { | ||
await auth.use('web').logout() | ||
return response.redirect().toRoute('pages:home') | ||
} | ||
|
||
async register({ request, auth, response }: HttpContext) { | ||
const { email, password } = await request.validateUsing(registerWithCredentialsValidator) | ||
|
||
const user = await this.userService.createUserWithCredentials(email, password) | ||
await auth.use('web').login(user) | ||
|
||
return response.redirect().toRoute('pages:auth.verify') | ||
} | ||
|
||
async retryEmailVerification({ auth, response }: HttpContext) { | ||
const user = auth.getUserOrFail() | ||
|
||
const listener = new SendVerificationEmail() | ||
listener.handle(new UserCreated(user)) | ||
|
||
return response.redirect().toRoute('pages:auth.verify') | ||
} | ||
|
||
async callbackForEmailVerification({ request, response }: HttpContext) { | ||
const { email } = await request.validateUsing(emailVerificationCallbackValidator) | ||
await this.userService.verifyEmail(email) | ||
|
||
return response.redirect().toRoute('actions:auth.verify.success') | ||
} | ||
|
||
// SOCIAL AUTHENTICATION | ||
|
||
// async initiateGithubLogin({ ally, inertia }: HttpContext) { | ||
// const url = await ally.use('github').redirectUrl() | ||
// return inertia.location(url) | ||
// } | ||
|
||
// async callbackForGithubLogin({ ally }: HttpContext) { | ||
// const github = ally.use('github') | ||
// const user = await github.user() | ||
|
||
// const data = await socialAccountLoginValidator.validate(user) | ||
// console.log(data) | ||
|
||
// const account = await getOrCreate({ | ||
// provider: 'github', | ||
// providerId: data.id, | ||
// }) | ||
|
||
// return response.json({ user, account: account.serialize() }) | ||
// } | ||
|
||
// async initiateGoogleLogin({ ally, inertia }: HttpContext) { | ||
// const url = await ally.use('google').redirectUrl() | ||
// return inertia.location(url) | ||
// } | ||
|
||
// async callbackForGoogleLogin({ response, ally }: HttpContext) { | ||
// const google = ally.use('google') | ||
// const user = await google.user() | ||
|
||
// return response.json({ user }) | ||
// } | ||
|
||
// async initiateLinkedinLogin({ ally, inertia }: HttpContext) { | ||
// const url = await ally.use('linkedin').redirectUrl() | ||
// return inertia.location(url) | ||
// } | ||
|
||
// async callbackForLinkedinLogin({ response, ally }: HttpContext) { | ||
// const linkedin = ally.use('linkedin') | ||
// const user = await linkedin.user() | ||
|
||
// return response.json({ user }) | ||
// } | ||
} |
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,8 @@ | ||
import User from '#models/user' | ||
import { BaseEvent } from '@adonisjs/core/events' | ||
|
||
export default class UserCreated extends BaseEvent { | ||
constructor(public readonly user: User) { | ||
super() | ||
} | ||
} |
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,8 @@ | ||
import type User from '#models/user' | ||
import { BaseEvent } from '@adonisjs/core/events' | ||
|
||
export default class UserEmailVerified extends BaseEvent { | ||
constructor(public readonly user: User) { | ||
super() | ||
} | ||
} |
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,8 @@ | ||
import { Exception } from '@adonisjs/core/exceptions' | ||
|
||
export default class AuthenticationDisabledException extends Exception { | ||
static status = 403 | ||
static code = "E_AUTH_DISABLED" | ||
static message = "Authentication is disabled" | ||
static help = "Did you forget to enable authentication in your .env file?" | ||
} |
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,23 @@ | ||
import UserCreated from "#events/user_created"; | ||
import EmailVerificationNotification from "#mails/email_verification_notification"; | ||
import mail from "@adonisjs/mail/services/main"; | ||
import { buildUrl, staticUrl } from "../url.js"; | ||
|
||
export default class SendVerificationEmail { | ||
async handle(event: UserCreated) { | ||
// Don't send the verification e-mail if the user has already verified it | ||
if (event.user.emailVerifiedAt) return; | ||
|
||
const email = event.user.email; | ||
const notification = new EmailVerificationNotification({ | ||
email, | ||
logoUrl: staticUrl("/images/logo-white.png"), | ||
|
||
verificationLink: buildUrl() | ||
.qs({ email }) | ||
.makeSigned("actions:auth.verify.callback", { expiresIn: "1h" }), | ||
}); | ||
|
||
await mail.send(notification); | ||
} | ||
} |
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,18 @@ | ||
import { BaseMail } from '@adonisjs/mail' | ||
import { render } from '@react-email/components' | ||
import type { JSX } from 'react' | ||
|
||
type JSXImport<T = {}> = () => Promise<{ default: (props: T) => JSX.Element }> | ||
|
||
export abstract class ReactNotification extends BaseMail { | ||
async jsx<T = {}>(importer: JSXImport<T>, props: NoInfer<T>) { | ||
const component = await importer().then((mod) => mod.default) | ||
const element = component(props) | ||
|
||
this.message | ||
.html(await render(element)) | ||
.text(await render(element, { plainText: true })) | ||
} | ||
|
||
abstract prepare(): void | Promise<void>; | ||
} |
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,14 @@ | ||
import { ReactNotification } from './base/react_notification.js' | ||
import type { EmailVerificationProps } from '#resources/emails/auth/email_verification' | ||
|
||
export default class EmailVerificationNotification extends ReactNotification { | ||
constructor(private props: EmailVerificationProps) { | ||
super() | ||
} | ||
|
||
async prepare() { | ||
this.message.to(this.props.email).subject('Confirma o teu e-mail!') | ||
|
||
await this.jsx(() => import('#resources/emails/auth/email_verification'), this.props) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
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,8 @@ | ||
export const messages = { | ||
auth: { | ||
oauth: { | ||
accessDenied: 'O pedido de início de sessão foi rejeitado.', | ||
stateMismatch: 'Ocorreu um erro ao iniciar sessão. Por favor, tenta novamente.', | ||
}, | ||
}, | ||
} as const |
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,12 @@ | ||
import type { HttpContext } from '@adonisjs/core/http' | ||
import type { NextFn } from '@adonisjs/core/types/http' | ||
|
||
export default class AutomaticSubmitMiddleware { | ||
async handle({ request, response, view }: HttpContext, next: NextFn) { | ||
const method = request.method() | ||
if (method === "POST") return next() | ||
|
||
// Clever hack by virk to render Edge.js templates in middlewares | ||
return response.status(200).send(await view.render('automatic_submit')) | ||
} | ||
} |
Oops, something went wrong.