123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- use crate::{key, schema, server::session::SessionHelper};
- use microrm::prelude::*;
- use serde::{Deserialize, Serialize};
- mod api;
- mod authorize;
- mod token;
- type Request = tide::Request<super::ServerStateWrapper>;
- const AUTHORIZE_PATH: &str = "oidc/authorize";
- const TOKEN_PATH: &str = "oidc/token";
- const JWKS_PATH: &str = "oidc/jwks";
- const DISCOVERY_PATH: &str = ".well-known/openid-configuration";
- #[derive(serde::Serialize)]
- pub enum OIDCErrorType {
- InvalidRequest,
- UnauthorizedClient,
- AccessDenied,
- UnsupportedResponseType,
- // InvalidScope,
- ServerError,
- // TemporarilyUnavailable,
- }
- pub enum OIDCErrorPayload<'a> {
- Borrowed(&'a str),
- Owned(String),
- }
- impl<'a> OIDCErrorPayload<'a> {
- fn as_str(&self) -> &str {
- match self {
- Self::Borrowed(s) => s,
- Self::Owned(s) => s.as_str(),
- }
- }
- }
- impl<'a> From<&'a str> for OIDCErrorPayload<'a> {
- fn from(value: &'a str) -> Self {
- Self::Borrowed(value)
- }
- }
- impl<'a> From<String> for OIDCErrorPayload<'a> {
- fn from(value: String) -> Self {
- Self::Owned(value)
- }
- }
- /// error type,
- pub struct OIDCError<'a>(OIDCErrorType, OIDCErrorPayload<'a>, Option<&'a str>);
- impl<'a> OIDCError<'a> {
- fn into_response(self) -> tide::Response {
- #[derive(Serialize)]
- struct ErrorOut<'a> {
- error: OIDCErrorType,
- error_description: &'a str,
- state: Option<&'a str>,
- }
- let eo = ErrorOut {
- error: self.0,
- error_description: self.1.as_str(),
- state: self.2,
- };
- tide::Response::builder(400)
- .body(serde_json::to_vec(&eo).unwrap())
- .build()
- }
- }
- impl<'a> From<microrm::Error> for OIDCError<'a> {
- fn from(value: microrm::Error) -> Self {
- Self(
- OIDCErrorType::ServerError,
- format!("Internal database error: {value}").into(),
- None,
- )
- }
- }
- async fn authorize(request: Request) -> tide::Result<tide::Response> {
- #[derive(Deserialize)]
- struct State {
- state: Option<String>,
- }
- let state: Option<String> = request.query::<State>().ok().and_then(|x| x.state);
- match authorize::do_authorize(request, state.as_deref()) {
- Ok(r) => Ok(r),
- Err(e) => Ok(e.into_response()),
- }
- }
- async fn token(request: Request) -> tide::Result<tide::Response> {
- match token::do_token(request).await {
- Ok(res) => Ok(res),
- Err(e) => Ok(e.into_response()),
- }
- }
- async fn jwks(request: Request) -> tide::Result<tide::Response> {
- let shelper = SessionHelper::new(&request);
- let realm = shelper.get_realm()?;
- // build JWK set
- let mut jwkset = jsonwebtoken::jwk::JwkSet { keys: vec![] };
- for key in realm.keys.get()?.into_iter() {
- if *key.key_state.as_ref() == schema::KeyState::Retired {
- continue;
- }
- // skip HMAC keys
- if let key::KeyType::HMac(_) = *key.key_type.as_ref() {
- continue;
- }
- jwkset.keys.push(key.wrapped().into_jwk());
- }
- Ok(tide::Response::builder(200)
- .header(tide::http::headers::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
- .content_type(tide::http::mime::JSON)
- .body(serde_json::to_vec(&jwkset).unwrap())
- .build())
- }
- async fn discovery_config(request: Request) -> tide::Result<tide::Response> {
- let server_config = &request.state().core.config;
- let base_url = format!(
- "{}/{}",
- server_config.base_url,
- request.param("realm").unwrap()
- );
- let config_response = serde_json::json!({
- "issuer": base_url,
- "authorization_endpoint": format!("{}/{}", base_url, AUTHORIZE_PATH),
- "token_endpoint": format!("{}/{}", base_url, TOKEN_PATH),
- "jwks_uri": format!("{}/{}", base_url, JWKS_PATH),
- "token_endpoint_auth_signing_alg_values_supported": ["EdDSA", "RS256"],
- "response_types_supported": ["code", "id_token", "token id_token"],
- "subject_types_supported": ["public"],
- "id_token_signing_alg_values_supported": ["EdDSA", "RS256"],
- });
- Ok(tide::Response::builder(200)
- .header(tide::http::headers::ACCESS_CONTROL_ALLOW_ORIGIN, "*")
- .body(config_response)
- .build())
- }
- pub(super) fn oidc_server(mut route: tide::Route<super::ServerStateWrapper>) {
- route.at(AUTHORIZE_PATH).get(authorize).post(authorize);
- route.at(TOKEN_PATH).post(token);
- route.at(JWKS_PATH).get(jwks);
- route.at(DISCOVERY_PATH).get(discovery_config);
- }
|