1use crate::error::OidcError;
2use openidconnect::{
3 AdditionalClaims, Audience, ClientId, ClientSecret, GenderClaim, IssuerUrl, Scope,
4 UserInfoClaims,
5};
6use serde::{Deserialize, Deserializer, Serialize};
7use std::collections::HashMap;
8
9#[derive(Deserialize, Serialize, Clone, Default)]
10#[serde(rename_all = "snake_case")]
11pub enum UserIdClaim {
12 Sub,
14 #[default]
16 PreferredUsername,
17 Email,
19}
20
21impl UserIdClaim {
22 pub fn extract_user_id<AC: AdditionalClaims, GC: GenderClaim>(
23 &self,
24 claims: &UserInfoClaims<AC, GC>,
25 ) -> Result<String, OidcError> {
26 Ok(match self {
27 Self::Sub => claims.subject().to_string(),
28 Self::PreferredUsername => claims
29 .preferred_username()
30 .ok_or(OidcError::Other("Missing preferred_username claim"))?
31 .to_string(),
32 Self::Email => claims
33 .email()
34 .ok_or(OidcError::Other("Missing email claim"))?
35 .to_string(),
36 })
37 }
38}
39
40fn deserialize_client_id<'de, D>(deserializer: D) -> Result<ClientId, D::Error>
43where
44 D: Deserializer<'de>,
45{
46 #[derive(Deserialize)]
47 #[serde(untagged)]
48 enum StringOrInt {
49 String(String),
50 Integer(i64),
51 }
52
53 let string_val = match StringOrInt::deserialize(deserializer)? {
54 StringOrInt::String(s) => s,
55 StringOrInt::Integer(i) => i.to_string(),
56 };
57
58 Ok(ClientId::new(string_val))
59}
60
61#[derive(Deserialize, Serialize, Clone)]
62#[serde(deny_unknown_fields)]
63pub struct OidcConfig {
64 pub name: String,
65 pub issuer: IssuerUrl,
66 #[serde(deserialize_with = "deserialize_client_id")]
67 pub client_id: ClientId,
68 pub client_secret: Option<ClientSecret>,
69 pub scopes: Vec<Scope>,
70 #[serde(default)]
71 pub allow_sign_up: bool,
72 pub require_group: Option<String>,
73 #[serde(default)]
74 pub claim_userid: UserIdClaim,
75 #[serde(default)]
76 pub additional_audiences: Vec<Audience>,
77 #[serde(default)]
78 pub assign_memberships: HashMap<String, Vec<String>>,
79}