//! HTML OAuth2 (Google) sign-in. //! //! The provider round-trip is handled by loco-oauth2's built-in authorize + //! cookie-callback handlers. The callback upserts the user, stores an OAuth2 //! session, sets loco-oauth2's *private* session cookie, and redirects to the //! configured `protected_url` — which is our [`complete`] handler. There we //! trade the OAuth2 session for OUR Loco `auth_token` JWT cookie, so the rest of //! the app (the Casbin layer, `guard`, the unified `/login`) treats a Google //! user exactly like a password login. Admins (matching `ADMIN_EMAIL`) land on //! the dashboard, everyone else on the storefront. use loco_oauth2::controllers::{ middleware::OAuth2CookieUser, oauth2::{google_authorization_url, google_callback_cookie}, }; use loco_rs::prelude::*; use crate::{ controllers::auth as auth_controller, models::{o_auth2_sessions, users, users::OAuth2UserProfile}, shared::guard, }; type GoogleCookieUser = OAuth2CookieUser; /// Bridge from loco-oauth2's session cookie to our own auth cookie. #[debug_handler] async fn complete(State(ctx): State, user: GoogleCookieUser) -> Result { let user: &users::Model = user.as_ref(); let jwt_secret = ctx.config.get_jwt_config()?; let token = user .generate_jwt(&jwt_secret.secret, jwt_secret.expiration) .or_else(|_| unauthorized("unauthorized!"))?; let dest = if guard::is_admin(&ctx, user) { "/admin/dashboard" } else { "/" }; format::render() .cookies(&[auth_controller::auth_cookie(&token, jwt_secret.expiration)])? .redirect(dest) } pub fn routes() -> Routes { Routes::new() .prefix("api/oauth2") // Redirects the browser to Google's consent screen. .add("/google", get(google_authorization_url)) // Google redirects back here; loco-oauth2 exchanges the code, upserts // the user, and redirects to `protected_url` (/api/oauth2/protected). .add( "/google/callback/cookie", get(google_callback_cookie::), ) .add("/protected", get(complete)) }