Skip to content

Commit

Permalink
Session auth request guard
Browse files Browse the repository at this point in the history
  • Loading branch information
sdankel committed May 2, 2024
1 parent 1b587f0 commit 26f83fa
Show file tree
Hide file tree
Showing 18 changed files with 224 additions and 280 deletions.
50 changes: 50 additions & 0 deletions app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"react-use-cookie": "^1.5.0",
"typed-axios-instance": "^3.3.1",
"typescript": "^4.9.5",
"usehooks-ts": "^3.0.2",
Expand Down
1 change: 0 additions & 1 deletion app/src/features/tokens/hooks/useApiTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { useGithubAuth } from '../../toolbar/hooks/useGithubAuth';
import axios from 'axios';
import HTTP, {
CreateTokenResponse,
DeleteTokenResponse,
RawToken,
TokensResponse,
} from '../../../utils/http';
Expand Down
18 changes: 10 additions & 8 deletions app/src/features/toolbar/hooks/useGithubAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useLocalSession } from '../../../utils/localStorage';
import { SERVER_URI } from '../../../constants';
import useCookie from 'react-use-cookie';
import axios from 'axios';
import HTTP, {
AuthenticatedUser,
Expand All @@ -13,13 +14,15 @@ export function useGithubAuth(): [
AuthenticatedUser | null,
() => Promise<void>
] {
const [sessionId, setSessionId] = useCookie('session');
const [githubUser, setGithubUser] = useState<AuthenticatedUser | null>(null);
const { githubCode, saveGithubCode, clearGithubCode } = useLocalSession();
const [searchParams, setSearchParams] = useSearchParams();

const logout = useCallback(async () => {
await HTTP.post(`/logout`);
setSessionId('');
setGithubUser(null);
HTTP.post(`/logout`);
}, [setGithubUser]);

// If this was a redirect from Github, we have a code to log in with.
Expand All @@ -43,21 +46,20 @@ export function useGithubAuth(): [
if (data.user) {
setGithubUser(data.user);
}
});
}).catch(() => clearGithubCode());
}, [githubCode, setGithubUser, clearGithubCode]);

// Attempt to fetch the logged in user info.
useEffect(() => {
if (!!githubUser) {
// Attempt to fetch the logged in user info if the session cookie is set and the user hasn't been fetched.
if (!!githubUser || !sessionId) {
return;
}

HTTP.get(`/user`).then(({ data }) => {
if (data.user) {
setGithubUser(data.user);
}
setGithubUser(data.user);
});
}, [githubUser, setGithubUser]);
}, [githubUser, setGithubUser, sessionId]);

return [githubUser, logout];
}

22 changes: 4 additions & 18 deletions app/src/utils/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,14 @@ export interface LoginRequest {
}

export interface LoginResponse {
sessionId?: string;
user?: AuthenticatedUser;
error?: string;
sessionId: string;
user: AuthenticatedUser;
}

export interface UserResponse {
user?: AuthenticatedUser;
error?: string;
user: AuthenticatedUser;
}

export interface GenericResponse {
error?: string;
}

export interface RawToken {
id: string,
name: string,
Expand All @@ -45,10 +39,6 @@ export interface GenericResponse {
error?: string;
}

export interface DeleteTokenResponse {
error?: string;
}

export interface TokensResponse {
tokens: RawToken[];
error?: string;
Expand All @@ -69,7 +59,6 @@ type Routes = [
{
route: '/logout';
method: 'POST';
jsonResponse: GenericResponse;
},
{
route: '/new_token';
Expand All @@ -85,7 +74,6 @@ type Routes = [
{
route: '/token/[id]';
method: 'DELETE';
jsonResponse: DeleteTokenResponse;
}
];

Expand All @@ -97,12 +85,10 @@ const HTTP: TypedAxios<Routes> = axios.create({
// Intercept the response and log any errors.
HTTP.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
if (response.data.error) {
console.log(`[${response.config.method}] API error: `, response.data.error);
}
return response;
}, function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
console.error('HTTP Error:', error);
return Promise.reject(error);
});
export default HTTP;
20 changes: 1 addition & 19 deletions src/api/api_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,12 @@ pub struct CreateTokenRequest {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateTokenResponse {
pub token: Option<Token>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}

/// The CreateToken request.
#[derive(Deserialize, Debug)]
pub struct DeleteTokenRequest {
pub id: String,
}

/// The response to a CreateToken request.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeleteTokenResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub token: Token,
}

/// The response to a CreateToken request.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TokensResponse {
pub tokens: Vec<Token>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
23 changes: 3 additions & 20 deletions src/api/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,6 @@ use uuid::Uuid;

use crate::models;

pub struct UserSessionId {
pub user_id: Uuid,
pub session_id: String,
}

#[derive(Serialize, Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
pub struct User {
Expand Down Expand Up @@ -42,25 +37,13 @@ pub struct LoginRequest {
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LoginResponse {
pub user: Option<User>,
pub session_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}

/// The response to a logout request.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LogoutResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub user: User,
pub session_id: String,
}

/// The response to a user GET request.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UserResponse {
pub user: Option<User>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub user: User,
}
36 changes: 19 additions & 17 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
pub mod auth;
pub mod api_token;

use rocket::serde::{Deserialize, Serialize};
use rocket::{http::Status, response::Responder, serde::{json::Json, Deserialize, Serialize}, Request};
use thiserror::Error;

/// A wrapper for API responses that can return errors.
pub type ApiResult<T> = Result<Json<T>, ApiError>;

/// An empty response.
#[derive(Serialize)]
pub struct EmptyResponse;

#[derive(Error, Debug)]
pub enum ApiError {
#[error("Unauthorized request")]
Unauthorized,
#[error("Missing session cookie")]
MissingCookie,
#[error("Database error: {0}")]
Database(#[from] crate::db::error::DatabaseError),
#[error("GitHub error: {0}")]
Github(#[from] crate::github::GithubError),
}

/// The publish request.
#[derive(Deserialize, Debug)]
pub struct PublishRequest {
pub name: String,
pub version: String,
impl<'r, 'o: 'r> Responder<'r, 'o> for ApiError {
fn respond_to(self, _request: &'r Request<'_>) -> rocket::response::Result<'o> {
match self {
ApiError::Database(_) => Err(Status::InternalServerError),
ApiError::Github(_) => Err(Status::Unauthorized),
}
}
}

/// The response to a publish request.
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublishResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
3 changes: 0 additions & 3 deletions src/db/api_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,6 @@ impl Database {
.select(models::Token::as_returning())
.load(connection)
.map_err(|_| DatabaseError::NotFound(user_id.to_string()))
// // TODO: fix return type
// eprintln!("res: {:?}", res);
// Err(DatabaseError::NotFound(user_id.to_string()))
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/db/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ pub enum DatabaseError {
NotFound(String),
#[error("Failed to save user: {0}")]
InsertUserFailed(String),
#[error("Failed to save session for user; {0}")]
#[error("Failed to save session for user: {0}")]
InsertSessionFailed(String),
#[error("Failed to save session for user; {0}")]
#[error("Failed to save token for user: {0}")]
InsertTokenFailed(String),
}
4 changes: 3 additions & 1 deletion src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
mod error;
pub mod error;
mod user_session;
mod api_token;

Expand All @@ -19,6 +19,7 @@ pub struct Database {
pub pool: DbPool,
}


impl Default for Database {
fn default() -> Self {
Database::new()
Expand All @@ -27,6 +28,7 @@ impl Default for Database {

impl Database {
pub fn new() -> Self {

// Create a connection pool
let pool = Pool::builder()
.build(ConnectionManager::<PgConnection>::new(db_url()))
Expand Down
Loading

0 comments on commit 26f83fa

Please sign in to comment.