arcly_http_identity/oidc/
client.rs1use std::collections::HashMap;
4
5use subtle::ConstantTimeEq;
6
7#[derive(Debug, Clone)]
9pub struct OidcClient {
10 pub client_id: String,
11 pub client_secret: Option<String>,
14 pub redirect_uris: Vec<String>,
17 pub allowed_scopes: Vec<String>,
18}
19
20impl OidcClient {
21 pub fn allows_redirect(&self, uri: &str) -> bool {
22 self.redirect_uris.iter().any(|u| u == uri)
23 }
24 pub fn requires_pkce(&self) -> bool {
26 self.client_secret.is_none()
27 }
28}
29
30#[derive(Debug, Clone)]
32pub struct ClientAuth {
33 pub client_id: String,
34 pub client_secret: Option<String>,
36}
37
38#[derive(Default)]
40pub struct OidcClientRegistry {
41 clients: HashMap<String, OidcClient>,
42}
43
44impl OidcClientRegistry {
45 pub fn new() -> Self {
46 Self::default()
47 }
48
49 pub fn register(mut self, client: OidcClient) -> Self {
50 self.clients.insert(client.client_id.clone(), client);
51 self
52 }
53
54 pub fn get(&self, client_id: &str) -> Option<&OidcClient> {
55 self.clients.get(client_id)
56 }
57
58 pub fn authenticate(&self, auth: &ClientAuth) -> Option<&OidcClient> {
62 let client = self.clients.get(&auth.client_id)?;
63 match (&client.client_secret, &auth.client_secret) {
64 (Some(expected), Some(presented)) => {
66 let ok: bool = expected.as_bytes().ct_eq(presented.as_bytes()).into();
67 ok.then_some(client)
68 }
69 (None, None) => Some(client),
71 _ => None,
73 }
74 }
75}