authorize.rs 5.5 KB

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