Skip to content

Commit

Permalink
refresh token endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
j-chad committed Sep 30, 2023
1 parent 4502e95 commit 57983c4
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 5 deletions.
26 changes: 22 additions & 4 deletions backend/src/api/auth/controllers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use super::models::RegisterNewUserRequest;
use crate::api::auth::models::UserResponse;
use crate::api::auth::models::{RefreshTokenRequest, UserResponse};
use crate::api::auth::utils::{find_refresh_token, find_user_by_id};
use crate::api::error::ErrorType::Unauthorized;
use crate::api::middleware::CurrentUser;
use crate::api::utils::friendly_id::{ItemIdType, ToFriendlyId};
use crate::{
Expand Down Expand Up @@ -135,11 +137,27 @@ pub async fn logout() -> impl IntoResponse {
content = RefreshTokenRequest
),
responses(
(status = 501, description = "Not Implemented")
)
)]
pub async fn refresh_token() -> impl IntoResponse {
StatusCode::NOT_IMPLEMENTED
pub async fn refresh_token(
State(state): State<AppState>,
Json(payload): Json<RefreshTokenRequest>,
) -> Result<(StatusCode, Json<AuthResponse>), APIError> {
let mut conn = get_db_connection(&state.database_pool).await?;

let token = find_refresh_token(&mut conn, &payload.refresh_token).await?;
let token = token.filter(|token| !token.is_expired()).ok_or(
APIErrorBuilder::new(Unauthorized)
.detail("The token you provided is expired.")
.build(),
)?;

let user = find_user_by_id(&mut conn, &token.user_id).await?;

let tokens = generate_auth_tokens(&mut conn, &user, &state.settings.auth.private_key).await?;

Ok((StatusCode::OK, Json(tokens)))
}

/// Get the current user
Expand Down
31 changes: 30 additions & 1 deletion backend/src/api/auth/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::api::auth::models::AuthResponse;
use crate::api::error::ErrorType::UserAlreadyExists;
use crate::api::error::ErrorType::{Unauthorized, UserAlreadyExists};
use crate::api::error::{APIError, APIErrorBuilder};
use crate::db::database::Connection;
use crate::db::refresh_token::{NewRefreshToken, RefreshToken};
Expand Down Expand Up @@ -138,6 +138,35 @@ pub async fn find_user_by_email(
})
}

pub async fn find_user_by_id(conn: &mut Connection, id: &Uuid) -> Result<User, APIError> {
User::all().find(id).first(conn).await.map_err(|e| {
error!(error = %e, "failed to find user by id");
APIErrorBuilder::from_error(e).build()
})
}

pub async fn find_refresh_token(
conn: &mut Connection,
refresh_token: &str,
) -> Result<Option<RefreshToken>, APIError> {
let uuid = Uuid::parse_str(refresh_token).map_err(|e| {
warn!(error = %e, "failed to parse refresh token");
APIErrorBuilder::new(Unauthorized)
.cause(e)
.detail("The token you provided is invalid.")
.build()
})?;

let token: Option<RefreshToken> = refresh_tokens::table
.filter(refresh_tokens::id.eq(uuid))
.first::<RefreshToken>(conn)
.await
.optional()
.map_err(|e| APIErrorBuilder::from_error(e).build())?;

Ok(token)
}

fn generate_auth_token(user: &User, private_key: &str) -> Result<String, ClaimError> {
let mut claims = Claims::new_expires_in(&TOKEN_EXPIRY_TIME)?;
claims.issuer("domus-api.jacksonc.dev")?;
Expand Down
6 changes: 6 additions & 0 deletions backend/src/db/refresh_token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ pub struct RefreshToken {
pub updated_at: Option<chrono::NaiveDateTime>,
}

impl RefreshToken {
pub(crate) fn is_expired(&self) -> bool {
self.expires_at < chrono::Utc::now().naive_utc()
}
}

#[derive(Insertable)]
#[diesel(table_name = crate::db::schema::refresh_tokens)]
pub struct NewRefreshToken {
Expand Down

0 comments on commit 57983c4

Please sign in to comment.