rauthy_client/handler/
mod.rs1use crate::build_lax_cookie_300;
2use crate::cookie_state::{OIDC_STATE_COOKIE, OidcCookieState};
3use crate::provider::OidcProvider;
4use crate::rauthy_error::RauthyError;
5use crate::token_set::OidcTokenSet;
6use crate::tokens::claims::{AccessToken, IdToken};
7use serde::{Deserialize, Serialize};
8use std::borrow::Cow;
9use tracing::error;
10
11#[cfg(feature = "axum")]
12pub mod axum;
13
14#[cfg(feature = "actix-web")]
15pub mod actix_web;
16
17#[derive(Debug, Deserialize)]
19pub struct OidcCallbackParams {
20 pub code: String,
21 pub state: String,
22}
23
24#[derive(Debug, Serialize)]
25struct OidcCodeRequestParams {
26 client_id: String,
27 client_secret: Option<String>,
28 code: String,
29 code_verifier: String,
30 grant_type: &'static str,
31 redirect_uri: String,
32}
33
34#[derive(Debug, PartialEq)]
36pub enum OidcCookieInsecure {
37 Yes,
38 No,
39}
40
41#[derive(Debug, PartialEq)]
43pub enum OidcSetRedirectStatus {
44 Yes,
45 No,
46}
47
48impl OidcCodeRequestParams {
49 pub async fn try_new(
50 code: String,
51 code_verifier: String,
52 redirect_uri: String,
53 ) -> Result<Self, RauthyError> {
54 let cfg = OidcProvider::config()?;
55 let client_id = cfg.client_id.clone();
56 let client_secret = cfg.secret.clone();
57 Ok(Self {
58 client_id,
59 client_secret,
60 code,
61 code_verifier,
62 grant_type: "authorization_code",
63 redirect_uri,
64 })
65 }
66}
67
68#[cfg(not(any(feature = "axum", feature = "actix-web")))]
79pub async fn validate_principal_generic(
80 principal: Option<crate::principal::PrincipalOidc>,
81 enc_key: &[u8],
82 insecure: OidcCookieInsecure,
83) -> Result<(), Option<(String, String)>> {
84 if principal.is_some() {
85 Ok(())
86 } else {
87 let (cookie_state, challenge) = OidcCookieState::generate();
88 let loc = {
89 let base = match OidcProvider::config() {
90 Ok(c) => &c.auth_url_base,
91 Err(_) => {
92 return Err(None);
93 }
94 };
95 format!(
96 "{base}&code_challenge={challenge}&nonce={}&state={}",
97 cookie_state.nonce, cookie_state.state
98 )
99 };
100
101 let value = cookie_state.to_encrypted_cookie_value(enc_key);
102 let cookie = build_lax_cookie_300(OIDC_STATE_COOKIE, &value, insecure);
103
104 Err(Some((loc, cookie)))
105 }
106}
107
108pub async fn oidc_callback(
115 cookie_state: OidcCookieState,
116 params: OidcCallbackParams,
117 insecure: OidcCookieInsecure,
118) -> Result<(String, OidcTokenSet, IdToken), RauthyError> {
119 if params.state != cookie_state.state {
121 return Err(RauthyError::BadRequest("Bad state"));
122 }
123
124 let (token_uri, redirect_uri) = {
125 let cfg = OidcProvider::config()?;
126 let t = cfg.provider.token_endpoint.clone();
127 let r = cfg.redirect_uri.clone();
128 (t, r)
129 };
130 let req_data = OidcCodeRequestParams::try_new(
131 params.code.clone(),
132 cookie_state.pkce_verifier,
133 redirect_uri,
134 )
135 .await?;
136
137 let res = OidcProvider::client()
138 .post(&token_uri)
139 .form(&req_data)
140 .send()
141 .await?;
142 if res.status().as_u16() >= 300 {
143 error!("{:?}", res);
144 let body = res.text().await;
145 let msg = match body {
146 Ok(value) => {
147 error!("raw OIDC provider response: {:?}", value);
148 value
149 }
150 Err(_) => "Internal Error - Bad response status".to_string(),
151 };
152
153 Err(RauthyError::Provider(Cow::from(msg)))
154 } else {
155 match res.json::<OidcTokenSet>().await {
156 Ok(ts) => {
157 let access_claims = AccessToken::from_token_validated(&ts.access_token).await?;
159
160 if ts.id_token.is_none() {
162 return Err(RauthyError::Provider(Cow::from("ID token is missing")));
163 }
164 let id_claims = IdToken::from_token_validated(
165 ts.id_token.as_deref().unwrap(),
166 &cookie_state.nonce,
167 )
168 .await?;
169
170 if access_claims.common.sub.is_none()
172 || access_claims.common.sub != id_claims.common.sub
173 {
174 return Err(RauthyError::InvalidClaims("Invalid `sub` claims"));
175 }
176
177 let cookie = build_lax_cookie_300(OIDC_STATE_COOKIE, "", insecure);
179
180 Ok((cookie, ts, id_claims))
181 }
182 Err(err) => {
183 error!("Deserializing OIDC response to OidcTokenSet: {}", err);
184 Err(RauthyError::Provider(Cow::from(
185 "Internal Error - Deserializing OIDC response",
186 )))
187 }
188 }
189 }
190}