authorize.rs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. use super::{api, OIDCError, OIDCErrorType, Request};
  2. use crate::{client::ClientExt, config, schema, server::session::SessionHelper};
  3. use microrm::prelude::*;
  4. fn do_code_authorize<'l, 's>(
  5. lease: &mut microrm::ConnectionLease,
  6. config: &config::Config,
  7. realm: &microrm::Stored<schema::Realm>,
  8. client: &microrm::Stored<schema::Client>,
  9. user: &microrm::Stored<schema::User>,
  10. scopes: impl Iterator<Item = &'l str>,
  11. redirect_uri: String,
  12. state: Option<&'s str>,
  13. ) -> Result<tide::Response, OIDCError<'s>> {
  14. let expiry =
  15. std::time::SystemTime::now() + std::time::Duration::from_secs(config.auth_code_expiry);
  16. let rng = ring::rand::SystemRandom::new();
  17. let raw_auth_code: [u8; 32] = ring::rand::generate(&rng)
  18. .map_err(|_| {
  19. OIDCError(
  20. OIDCErrorType::ServerError,
  21. "failed to generate auth code".into(),
  22. state,
  23. )
  24. })?
  25. .expose();
  26. let encoded_auth_code = base64::encode_config(raw_auth_code, base64::URL_SAFE_NO_PAD);
  27. realm.auth_codes.insert(
  28. lease,
  29. schema::AuthCode {
  30. realm: realm.id(),
  31. client: client.id(),
  32. user: user.id(),
  33. scopes: scopes
  34. .map(|x| x.to_owned())
  35. .collect::<Vec<_>>()
  36. .into_serialized(),
  37. expiry: expiry.into(),
  38. redirect_uri: redirect_uri.clone(),
  39. code: encoded_auth_code.clone(),
  40. },
  41. )?;
  42. let new_params = [
  43. ("response_type", "code"),
  44. ("code", encoded_auth_code.as_str()),
  45. ]
  46. .into_iter()
  47. .chain(state.map(|v| ("state", v)));
  48. Ok(tide::Redirect::temporary(
  49. tide::http::Url::parse_with_params(redirect_uri.as_str(), new_params).map_err(|e| {
  50. OIDCError(
  51. OIDCErrorType::InvalidRequest,
  52. format!("could not parse redirect_uri as a URL: {e}").into(),
  53. None,
  54. )
  55. })?,
  56. )
  57. .into())
  58. }
  59. pub(super) async fn do_authorize(
  60. request: Request,
  61. state: Option<&str>,
  62. ) -> Result<tide::Response, OIDCError> {
  63. let shelper = SessionHelper::new(&request);
  64. let mut lease = request.state().lease().await?;
  65. let realm = shelper.get_realm(&mut lease).map_err(|_| {
  66. OIDCError(
  67. OIDCErrorType::InvalidRequest,
  68. "No such realm!".into(),
  69. state,
  70. )
  71. })?;
  72. let make_redirect = || {
  73. let mut login_url = request.url().join("../v1/session/login").unwrap();
  74. login_url
  75. .query_pairs_mut()
  76. .clear()
  77. .append_pair("redirect", request.url().as_str());
  78. Ok(tide::Redirect::new(login_url).into())
  79. };
  80. let qp: api::AuthorizationRequestQuery = request
  81. .query()
  82. .map_err(|x| OIDCError(OIDCErrorType::InvalidRequest, x.to_string().into(), state))?;
  83. // collect session authentication info
  84. let potential_sauth = shelper
  85. .get_session(&mut lease, &request)
  86. .and_then(|session| shelper.get_auth_for_session(&mut lease, realm.id(), &session));
  87. let Some(user_id) = potential_sauth.and_then(|v| v.user) else {
  88. // if we don't have any relevant auth info, redirect to login
  89. return make_redirect();
  90. };
  91. let Ok(Some(user)) = realm.users.with_id(user_id).first().get(&mut lease) else {
  92. return Err(OIDCError(
  93. OIDCErrorType::ServerError,
  94. "Internal state error!".into(),
  95. state,
  96. ));
  97. };
  98. // verify the client_id refers to an extant client
  99. let client = realm
  100. .clients
  101. .with(schema::Client::Shortname, &qp.client_id)
  102. .first()
  103. .get(&mut lease)
  104. .ok()
  105. .flatten()
  106. .ok_or_else(|| {
  107. OIDCError(
  108. OIDCErrorType::UnauthorizedClient,
  109. "client does not exist".into(),
  110. state,
  111. )
  112. })?;
  113. let scopes = qp.scope.as_deref().unwrap_or("").split_whitespace();
  114. // check that redirect URI matches
  115. match client.check_redirect(&mut lease, qp.redirect_uri.as_str()) {
  116. Ok(true) => (),
  117. Ok(false) => {
  118. return Err(OIDCError(
  119. OIDCErrorType::InvalidRequest,
  120. "invalid redirect URI".into(),
  121. state,
  122. ))
  123. }
  124. Err(_) => {
  125. return Err(OIDCError(
  126. OIDCErrorType::ServerError,
  127. "invalid stored redirect uri".into(),
  128. state,
  129. ))
  130. }
  131. }
  132. if qp.response_type == "code" {
  133. do_code_authorize(
  134. &mut lease,
  135. &request.state().core.config,
  136. &realm,
  137. &client,
  138. &user,
  139. scopes,
  140. qp.redirect_uri,
  141. state,
  142. )
  143. } else if qp.response_type == "token" {
  144. let rhelper = request
  145. .state()
  146. .core
  147. .realms
  148. .get_helper(&mut lease, realm.id())
  149. .unwrap();
  150. let token = rhelper
  151. .generate_access_token(&mut lease, &client, &user, scopes)
  152. .map_err(|e| {
  153. OIDCError(
  154. OIDCErrorType::ServerError,
  155. format!("could not generate token: {e}").into(),
  156. state,
  157. )
  158. })?;
  159. Ok(tide::Response::builder(200)
  160. .content_type(tide::http::mime::JSON)
  161. .body(
  162. serde_json::to_vec(&api::TokenResponse {
  163. token_type: "bearer",
  164. access_token: token.as_str(),
  165. refresh_token: None,
  166. scope: None,
  167. })
  168. .unwrap(),
  169. )
  170. .build())
  171. } else {
  172. Err(OIDCError(
  173. OIDCErrorType::UnsupportedResponseType,
  174. "only 'code' and 'token' are understood".into(),
  175. state,
  176. ))
  177. }
  178. }