-
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 #46 from erland-syafiq/auth-extensions
Extended Auth
- Loading branch information
Showing
17 changed files
with
381 additions
and
128 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 |
---|---|---|
@@ -1,4 +1,4 @@ | ||
# /applicants | ||
# /api/applicants | ||
|
||
## Base URL | ||
`/api/applicants` | ||
|
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,105 @@ | ||
# /api/login | ||
|
||
## Base URL | ||
`/api/login` | ||
|
||
## Overview | ||
The `/api/login` endpoint handles user authentication for admin access. | ||
|
||
## Endpoints | ||
|
||
### POST `/api/login` | ||
|
||
#### Description | ||
Authenticate a user with email and password to generate an encrypted token stored in a cookie. | ||
|
||
#### Request Body | ||
| Field | Type | Description | | ||
|---------------|--------|--------------------------------------| | ||
| `email` | string | The email of the user. | | ||
| `password` | string | The password of the user. | | ||
| `rememberMe` | bool | Extends length of login session. (Optional; Default: false) | | ||
|
||
#### Response Status Codes | ||
| Status Code | Description | | ||
|-------------|---------------------------| | ||
| 200 OK | Returns admin details. | | ||
| 401 Unauthorized | Invalid email or password. | | ||
|
||
#### Response Body | ||
| Field | Type | Description | | ||
|----------|--------|--------------------------------------| | ||
| `username` | string | The username of the user. | | ||
| `email`| string | The email of the user. | | ||
|
||
#### Example Request | ||
```http | ||
POST /api/login HTTP/1.1 | ||
Host: yourdomain.com | ||
Content-Type: application/json | ||
{ | ||
"email": "[email protected]", | ||
"password": "admin_password" | ||
} | ||
``` | ||
|
||
#### Example Response | ||
On success: | ||
```json | ||
{ | ||
"username": "Admin", | ||
"email": "[email protected]" | ||
} | ||
``` | ||
|
||
On failure: | ||
```json | ||
{ | ||
"message": "Failed validation" | ||
} | ||
``` | ||
|
||
### GET `/api/login` | ||
|
||
#### Description | ||
Check if user is authenticated as admin based on the presence and validity of the stored token in the cookie. | ||
|
||
#### Headers | ||
| Key | Value | | ||
|---------------|------------------------| | ||
| Authorization | Bearer `<JWT token>` | | ||
|
||
#### Response Status Codes | ||
| Status Code | Description | | ||
|-------------|-----------------------------------| | ||
| 200 OK | User authenticated as admin. | | ||
| 401 Unauthorized | Invalid cookie. | | ||
|
||
#### Response Body | ||
| Field | Type | Description | | ||
|----------|--------|--------------------------------------| | ||
| `username` | string | The username of the user. | | ||
| `email`| string | The email of the user. | | ||
|
||
#### Example Request | ||
```http | ||
GET /api/login HTTP/1.1 | ||
Host: yourdomain.com | ||
Authorization: Bearer <JWT token> | ||
``` | ||
|
||
#### Example Response | ||
On success: | ||
```json | ||
{ | ||
"username": "Admin", | ||
"email": "[email protected]" | ||
} | ||
``` | ||
|
||
On failure: | ||
```json | ||
{ | ||
"message": "Failed validation" | ||
} |
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,31 @@ | ||
# /api/login | ||
|
||
## Base URL | ||
`/api/logout` | ||
|
||
## Overview | ||
The `/api/logout` endpoint logs out user and clears user authentication cookies. | ||
|
||
## Endpoints | ||
|
||
### GET `/api/logout` | ||
|
||
#### Description | ||
Clears user authentication token. Always succeeds and returns status code 200 with no body. | ||
|
||
#### Headers | ||
| Key | Value | | ||
|---------------|------------------------| | ||
| Authorization | Bearer `<JWT token>` | | ||
|
||
#### Response Status Codes | ||
| Status Code | Description | | ||
|-------------|-----------------------------------| | ||
| 200 OK | User logged out. | | ||
|
||
#### Example Request | ||
```http | ||
GET /api/login HTTP/1.1 | ||
Host: yourdomain.com | ||
Authorization: Bearer <JWT token> | ||
``` |
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,43 @@ | ||
import { cookies } from "next/headers"; | ||
import { NextResponse } from "next/server"; | ||
import bcrypt from "bcryptjs"; | ||
import { getHashedAdminPassword, isUserAdmin, encrypt } from "@/app/utils/AuthUtils"; | ||
|
||
const { ADMIN_USERNAME } = process.env; | ||
|
||
const TOKEN_EXPIRATION_SHORT = 3 * 60 * 60 * 1000; // 3 hours | ||
const TOKEN_EXPIRATION_LONG = 30 * 24 * 60 * 60 * 1000; // 30 days | ||
|
||
export async function POST(request) { | ||
try { | ||
// Get email and password for login | ||
const body = await request.json(); | ||
const { email, password, rememberMe = false } = body; | ||
|
||
// Used bcrypt module to stop timing attacks | ||
if (email !== ADMIN_USERNAME || !await bcrypt.compare(password, await getHashedAdminPassword())) { | ||
return NextResponse.json({ message: "Unauthorized"}, { status: 401 }); | ||
} | ||
|
||
// Create encrypted token with user email and expiration time | ||
const expiresIn = rememberMe ? TOKEN_EXPIRATION_LONG : TOKEN_EXPIRATION_SHORT; | ||
const expires = new Date(Date.now() + expiresIn); | ||
const token = await encrypt({ email, expires }, expires); | ||
|
||
// Set cookies 'vtmunc_admin' for admin access | ||
cookies().set("vtmunc_admin", token, { expires, httpOnly: true }); | ||
|
||
return NextResponse.json({ username: "Admin", email: email}, { status: 200 }); | ||
} | ||
catch (e) { | ||
return NextResponse.json({ message: "Unauthorized"}, { status: 401 }); | ||
} | ||
} | ||
|
||
export async function GET(request) { | ||
if (!await isUserAdmin(request)) { | ||
return NextResponse.json({ message: "Invalid cookie"}, { status: 401 }); | ||
} | ||
|
||
return NextResponse.json({ username: "Admin", email: ADMIN_USERNAME}, { status: 200 }); | ||
} |
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,6 @@ | ||
import { cookies } from "next/headers"; | ||
|
||
export async function GET() { | ||
cookies().delete("vtmunc_admin"); | ||
return new Response("", {status: 200}); | ||
} |
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
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,84 @@ | ||
"use client" | ||
|
||
import { createContext, useContext, useEffect, useState } from "react" | ||
|
||
const AuthContext = createContext(); | ||
|
||
export default function AuthProvider({ children }) { | ||
const [user, setUser] = useState({}); | ||
|
||
const isAuthenticated = Object.keys(user).length > 0; | ||
|
||
// Attemps to auto login | ||
useEffect(() => { | ||
async function autoLogin() { | ||
try { | ||
const response = await fetch("/api/login"); | ||
if (!response.ok) { | ||
throw new Error("Invalid credentials"); | ||
} | ||
const user = await response.json(); | ||
setUser(user); | ||
} | ||
catch(e) { | ||
} | ||
} | ||
|
||
autoLogin(); | ||
}, []) | ||
|
||
|
||
async function login(email, password, rememberMe = false) { | ||
const response = await fetch('/api/login', { | ||
method: 'POST', | ||
headers: { 'Content-Type': 'application/json' }, | ||
body: JSON.stringify({ email, password, rememberMe }), | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error("Invalid email and password"); | ||
} | ||
} | ||
|
||
async function logout() { | ||
try { | ||
const response = await fetch("/api/logout"); | ||
|
||
if (!response.ok) { | ||
throw new Error("Server error"); | ||
} | ||
|
||
setUser({}); | ||
window.location.href = "/"; | ||
} | ||
catch (e) { | ||
} | ||
} | ||
|
||
|
||
|
||
return ( | ||
<AuthContext.Provider value={{user, isAuthenticated, login, logout}}> | ||
{children} | ||
</AuthContext.Provider> | ||
) | ||
} | ||
|
||
/** | ||
* Objects that can be obtained from useAuth | ||
* | ||
* user: { | ||
* email: string, | ||
* username: string | ||
* } | ||
* | ||
* login: (email: string, password: string, rememberMe?: boolean) => (); | ||
* Logs in user, doesn't automatically call setUser | ||
* Note: throws on server failure and on invalid email and password. Use with try catch statement to get error message | ||
* | ||
* logout: () => (); | ||
* Logouts out user and auto-directs to the home page | ||
*/ | ||
export function useAuth() { | ||
return useContext(AuthContext); | ||
} |
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.