Skip to content

Commit 2aa0ca5

Browse files
gyoumiJoshCLimKavikaPalletenneAlex_Miao_WSL
authored
Refactor: updated frontend auth to use cookies instead of localhost (#569)
* add 2024 sponsor logos * add tailwind prettier plugin * update eslint-plugin-prettier to compatible version * format and lint code * update eslint config * feat: add 2024 sponsor logos (#488) * remove grouped tailwind psuedo-classes * refactor(frontend): remove MUI theme and CSS baseline * format tailwind classes * set prettier config options * format according to prettier config * format tw macros via Tailwind functions config * AdminSideBar styles and width state rewritten * feat(AdminSidebar): migrate admin sidebar from MUI to Radix UI Primitives * feat(CampaignCreationPreview): migrate from MUI and remove bad CampaignForm preview * feat(AdminSidebar): add borders between organisations * refactor(BackgroundWrapper): delete unused component * feat(ApplicationPreviewer): migrate ApplicationPreviewer away from MUI * feat(CreateOrganisationForm): rewrite components using Tailwind * feat(Dropdown): remove unused imports * more migration * feat(frontend): update login/signup buttons to Coming Soon (#499) * feat(Signup): migrate to twin * feat(frontend): Update ESLint and Prettier config (#490) * add 2024 sponsor logos * add tailwind prettier plugin * update eslint-plugin-prettier to compatible version * format and lint code * update eslint config * remove grouped tailwind psuedo-classes * format tailwind classes * set prettier config options * format according to prettier config * format tw macros via Tailwind functions config * email sending * fix "recipient" spelling * cargo fmt * fix errors with `template_subject` introduction * cargo clippy fixes * application role preferences and updating applied roles * lock out user from application changes after submission * lock application after submission and campaign close date * fix uses of `LEFT JOIN` when `JOIN` was needed * make unsubmitted application viewable after campaign end * only submitted applications are viewable by reviewers * added `Answer`-related schemas to api.yaml * register new `ApplicationHandler` endpoints in app * completed up to /organisation/slug_check * `NewCampaign` model for campaign create request body * block applications for ended campaigns * `NewEmailTemplate` model for template request body * fix `assert_campaign_is_open()` naming * api.yaml up to `/organisation/{id}/logo` * update `api.json` up to `/organisation/{id}/logo` * rerouted application page to new backend and made prop error fixes for CampaignCard related components * rerouted application page to new backend * fixed all initial errors * fixed all initial errors * Move some `Rating` handler fn to `ApplicationHandler` * update `api.json` up to `/rating/{id}` * saving for now * created user context * created user context + preparing for work division * feat: implemented cookie wrapped auth token * fix: SQL sub query alias * saving prompt updates * more prompts * fixed merge * login working * only comming fe changes * saving state for now * idk why this didnt merge * idk why this didnt merge either * got dashboard working kind of lol * fixed frontend admin org route * fixed frontend admin org route * fixed readme conflict --------- Co-authored-by: Josh <joshconstantinelim@gmail.com> Co-authored-by: Kavika <kbpalletenne@gmail.com> Co-authored-by: Alex_Miao_WSL <yuan_sen.miao@student.unsw.edu.au> Co-authored-by: Kavika <kavika.palletenne@devsoc.app>
1 parent 350fd8e commit 2aa0ca5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+1180
-10225
lines changed

backend/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ target
33
Cargo.lock
44
prisma-cli/prisma/migrations
55
/.idea
6-
**/.DS_Store
6+
**/.DS_Store
7+
.env.*

backend/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,4 @@ Request -> Middleware (optional) -> Handler -> Service -> Middleware (Optional)
6161
- JWT
6262

6363
### Storage
64-
- Object storage
64+
- Object storage

backend/migrations/20240406023149_create_users.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ CREATE TABLE users (
1414
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
1515
);
1616

17+
-- Seed initial superuser
18+
INSERT INTO users (id, email, name, role)
19+
VALUES (10000, 'chaosdirectors@devsoc.app', 'Super Admin', 'SuperUser');
20+
1721
CREATE UNIQUE INDEX IDX_users_email_lower on users((lower(email)));

backend/server/src/handler/auth.rs

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,52 @@ use crate::service::auth::create_or_get_user_id;
1111
use crate::service::jwt::encode_auth_token;
1212
use axum::extract::{Query, State};
1313
use axum_extra::extract::cookie::{Cookie, CookieJar, Expiration};
14-
use axum::response::IntoResponse;
14+
use axum::response::{IntoResponse, Redirect};
1515
use oauth2::reqwest::async_http_client;
16-
use oauth2::{AuthorizationCode, TokenResponse};
16+
use oauth2::{AuthorizationCode, TokenResponse, Scope};
1717
use time::OffsetDateTime;
1818

19+
/// Handles the Google OAuth2 callback.
20+
///
21+
/// This handler processes the OAuth2 code received from Google after user authorization.
22+
/// It exchanges the code for an access token, retrieves the user's profile information,
23+
/// creates or retrieves the user in the database, and generates a JWT token for authentication.
24+
///
25+
/// # Arguments
26+
///
27+
/// * `state` - The application state
28+
/// * `query` - The OAuth2 callback query parameters containing the authorization code
29+
/// * `oauth_client` - The OAuth2 client for Google authentication
30+
///
31+
/// # Returns
32+
///
33+
/// * `Result<impl IntoResponse, ChaosError>` - JWT token or error
34+
///
35+
/// Initiates the Google OAuth2 flow.
36+
///
37+
/// This handler redirects users to Google's OAuth2 authorization URL to begin
38+
/// the authentication process.
39+
///
40+
/// # Arguments
41+
///
42+
/// * `state` - The application state containing the OAuth2 client
43+
///
44+
/// # Returns
45+
///
46+
/// * `Result<impl IntoResponse, ChaosError>` - Redirect to Google OAuth or error
47+
pub async fn google_auth_init(
48+
State(state): State<AppState>,
49+
) -> Result<impl IntoResponse, ChaosError> {
50+
let (auth_url, _csrf_token) = state.oauth2_client
51+
.authorize_url(|| oauth2::CsrfToken::new_random())
52+
.add_scope(Scope::new("openid".to_string()))
53+
.add_scope(Scope::new("email".to_string()))
54+
.add_scope(Scope::new("profile".to_string()))
55+
.url();
56+
57+
Ok(Redirect::to(auth_url.as_str()))
58+
}
59+
1960
/// Handles the Google OAuth2 callback.
2061
///
2162
/// This handler processes the OAuth2 code received from Google after user authorization.
@@ -75,8 +116,16 @@ pub async fn google_callback(
75116
.expires(Expiration::DateTime(OffsetDateTime::now_utc() + time::Duration::days(5))) // Set an expiration time of 5 days, TODO: read from env?
76117
.secure(!state.is_dev_env) // Send only over HTTPS, comment out for testing
77118
.path("/"); // Available for all paths
78-
// Add the cookie to the response
79-
Ok(jar.add(cookie))
119+
120+
// Redirect to the frontend dashboard after successful authentication
121+
let redirect_url = if state.is_dev_env {
122+
"http://localhost:3000/dashboard"
123+
} else {
124+
"/dashboard" // In production, this would be the full URL
125+
};
126+
127+
// Add the cookie and redirect
128+
Ok((jar.add(cookie), Redirect::to(redirect_url)))
80129
}
81130

82131
pub struct DevLoginHandler;
@@ -104,8 +153,12 @@ impl DevLoginHandler {
104153
.http_only(true) // Prevent JavaScript access
105154
.expires(Expiration::DateTime(OffsetDateTime::now_utc() + time::Duration::days(5))) // Set an expiration time of 5 days, TODO: read from env?
106155
.path("/"); // Available for all paths
107-
// Add the cookie to the response
108-
Ok(jar.add(cookie))
156+
157+
// Redirect to the frontend dashboard after successful authentication
158+
let redirect_url = "http://localhost:3000/dashboard";
159+
160+
// Add the cookie and redirect
161+
Ok((jar.add(cookie), Redirect::to(redirect_url)))
109162
}
110163

111164
pub async fn dev_org_admin_login(
@@ -130,8 +183,12 @@ impl DevLoginHandler {
130183
.http_only(true) // Prevent JavaScript access
131184
.expires(Expiration::DateTime(OffsetDateTime::now_utc() + time::Duration::days(5))) // Set an expiration time of 5 days, TODO: read from env?
132185
.path("/"); // Available for all paths
133-
// Add the cookie to the response
134-
Ok(jar.add(cookie))
186+
187+
// Redirect to the frontend dashboard after successful authentication
188+
let redirect_url = "http://localhost:3000/dashboard";
189+
190+
// Add the cookie and redirect
191+
Ok((jar.add(cookie), Redirect::to(redirect_url)))
135192
}
136193

137194
pub async fn dev_user_login(
@@ -156,7 +213,11 @@ impl DevLoginHandler {
156213
.http_only(true) // Prevent JavaScript access
157214
.expires(Expiration::DateTime(OffsetDateTime::now_utc() + time::Duration::days(5))) // Set an expiration time of 5 days, TODO: read from env?
158215
.path("/"); // Available for all paths
159-
// Add the cookie to the response
160-
Ok(jar.add(cookie))
216+
217+
// Redirect to the frontend dashboard after successful authentication
218+
let redirect_url = "http://localhost:3000/dashboard";
219+
220+
// Add the cookie and redirect
221+
Ok((jar.add(cookie), Redirect::to(redirect_url)))
161222
}
162223
}

backend/server/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ async fn main() -> Result<(), ChaosError> {
1010
dotenvy::dotenv()?;
1111

1212
let app = app().await?;
13-
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
13+
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
1414
axum::serve(listener, app).await.unwrap();
1515

1616
Ok(())
17-
}
17+
}

backend/server/src/models/app.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::handler::answer::AnswerHandler;
22
use crate::handler::application::ApplicationHandler;
3-
use crate::handler::auth::{google_callback, DevLoginHandler};
3+
use crate::handler::auth::{google_callback, google_auth_init, DevLoginHandler};
44
use crate::handler::campaign::CampaignHandler;
55
use crate::handler::email_template::EmailTemplateHandler;
66
use crate::handler::offer::OfferHandler;
@@ -123,6 +123,7 @@ pub async fn app() -> Result<Router, ChaosError> {
123123
.allow_credentials(true)
124124
.allow_origin([
125125
"http://localhost".parse().unwrap(),
126+
"http://localhost:3000".parse().unwrap(),
126127
"https://chaos.devsoc.app".parse().unwrap(),
127128
"http://chaos.devsoc.app".parse().unwrap(),
128129
"https://chaosstaging.devsoc.app".parse().unwrap(),
@@ -131,6 +132,7 @@ pub async fn app() -> Result<Router, ChaosError> {
131132

132133
Ok(Router::new()
133134
.route("/", get(|| async { "Join DevSoc! https://devsoc.app/" }))
135+
.route("/auth/google", get(google_auth_init))
134136
.route("/api/auth/callback/google", get(google_callback))
135137
.route("/api/v1/dev/super_admin_login", get(DevLoginHandler::dev_super_admin_login))
136138
.route("/api/v1/dev/org_admin_login", get(DevLoginHandler::dev_org_admin_login))

backend/server/src/service/auth.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,28 @@ pub async fn extract_user_id_from_request(
109109
) -> Result<i64, ChaosError> {
110110
let decoding_key = &state.decoding_key;
111111
let jwt_validator = &state.jwt_validator;
112+
113+
112114
let TypedHeader(cookies) = parts
113115
.extract::<TypedHeader<Cookie>>()
114116
.await
115-
.map_err(|_| ChaosError::NotLoggedIn)?;
117+
.map_err(|e| {
118+
ChaosError::NotLoggedIn
119+
})?;
116120

117-
let token = cookies.get("auth_token").ok_or(ChaosError::NotLoggedIn)?;
121+
122+
123+
let token = cookies.get("auth_token").ok_or_else(|| {
124+
ChaosError::NotLoggedIn
125+
})?;
126+
127+
118128

119129
let claims =
120-
decode_auth_token(token, decoding_key, jwt_validator).ok_or(ChaosError::NotLoggedIn)?;
130+
decode_auth_token(token, decoding_key, jwt_validator).ok_or_else(|| {
131+
ChaosError::NotLoggedIn
132+
})?;
121133

134+
122135
Ok(claims.sub)
123136
}

frontend/.env.development

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
VITE_OAUTH_CALLBACK_URL=https://accounts.google.com/o/oauth2/v2/auth?client_id=985448402284-al4vuqpokkhgv6h952lhu6iasg1lupug.apps.googleusercontent.com&redirect_uri=http://localhost:3000/auth/callback&response_type=code&scope=profile email&access_type=online
2-
VITE_API_BASE_URL=http://localhost:8000
1+
VITE_OAUTH_CALLBACK_URL=https://accounts.google.com/o/oauth2/v2/auth?client_id=731862014126-5b109p4v6b173910ib347gtfn0ecnacj.apps.googleusercontent.com&redirect_uri=http://localhost:8080/api/auth/callback/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile&access_type=offline
2+
VITE_API_BASE_URL=http://localhost:8080
33
BROWSER=none

frontend/.eslintrc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"parserOptions": {
2626
"ecmaVersion": 12,
2727
"sourceType": "module",
28-
"project": "./tsconfig.json"
28+
"project": "./frontend/tsconfig.json"
2929
},
3030
"plugins": ["react", "prettier"],
3131
"rules": {

frontend/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,6 @@
2323
npm-debug.log*
2424
yarn-debug.log*
2525
yarn-error.log*
26+
27+
.env.development
28+
.env

0 commit comments

Comments
 (0)