]>
git.proxmox.com Git - proxmox-openid-rs.git/blob - src/lib.rs
3 use anyhow
::{format_err, Error}
;
4 use serde
::{Deserialize, Serialize}
;
7 pub use http_client
::http_client
;
10 pub use auth_state
::*;
20 CoreAuthenticationFlow
,
40 /// Stores Additional Claims into a serde_json::Value;
41 #[derive(Debug, Deserialize, Serialize)]
42 pub struct GenericClaims(serde_json
::Value
);
43 impl AdditionalClaims
for GenericClaims {}
45 pub type GenericUserInfoClaims
= UserInfoClaims
<GenericClaims
, CoreGenderClaim
>;
47 #[derive(Debug, Deserialize, Serialize, Clone)]
48 pub struct OpenIdConfig
{
49 pub issuer_url
: String
,
50 pub client_id
: String
,
51 #[serde(skip_serializing_if="Option::is_none")]
52 pub client_key
: Option
<String
>,
53 #[serde(skip_serializing_if="Option::is_none")]
54 pub scopes
: Option
<Vec
<String
>>,
57 pub struct OpenIdAuthenticator
{
62 #[derive(Debug, Deserialize, Serialize)]
63 pub struct PublicAuthState
{
64 pub csrf_token
: CsrfToken
,
68 #[derive(Debug, Deserialize, Serialize)]
69 pub struct PrivateAuthState
{
70 pub csrf_token
: CsrfToken
,
72 pub pkce_verifier
: PkceCodeVerifier
,
76 impl PrivateAuthState
{
78 pub fn new() -> Self {
79 let nonce
= Nonce
::new_random();
80 let csrf_token
= CsrfToken
::new_random();
81 let (_pkce_challenge
, pkce_verifier
) = PkceCodeChallenge
::new_random_sha256();
87 ctime
: proxmox_time
::epoch_i64(),
91 pub fn pkce_verifier(&self) -> PkceCodeVerifier
{
92 // Note: PkceCodeVerifier does not impl. clone()
93 PkceCodeVerifier
::new(self.pkce_verifier
.secret().to_string())
96 pub fn pkce_challenge(&self) -> PkceCodeChallenge
{
97 PkceCodeChallenge
::from_code_verifier_sha256(&self.pkce_verifier
)
100 pub fn public_state_string(&self, realm
: String
) -> Result
<String
, Error
> {
101 let pub_state
= PublicAuthState
{
102 csrf_token
: self.csrf_token
.clone(),
105 Ok(serde_json
::to_string(&pub_state
)?
)
109 impl OpenIdAuthenticator
{
111 pub fn discover(config
: &OpenIdConfig
, redirect_url
: &str) -> Result
<Self, Error
> {
113 let client_id
= ClientId
::new(config
.client_id
.clone());
114 let client_key
= config
.client_key
.clone().map(|key
| ClientSecret
::new(key
));
115 let issuer_url
= IssuerUrl
::new(config
.issuer_url
.clone())?
;
117 let provider_metadata
= CoreProviderMetadata
::discover(&issuer_url
, http_client
)?
;
119 let client
= CoreClient
::from_provider_metadata(
123 ).set_redirect_uri(RedirectUrl
::new(String
::from(redirect_url
))?
);
127 config
: config
.clone(),
131 pub fn authorize_url(&self, state_dir
: &str, realm
: &str) -> Result
<String
, Error
> {
133 let private_auth_state
= PrivateAuthState
::new();
134 let public_auth_state
= private_auth_state
.public_state_string(realm
.to_string())?
;
135 let nonce
= private_auth_state
.nonce
.clone();
137 store_auth_state(Path
::new(state_dir
), realm
, &private_auth_state
)?
;
139 // Generate the authorization URL to which we'll redirect the user.
140 let mut request
= self.client
142 CoreAuthenticationFlow
::AuthorizationCode
,
143 || CsrfToken
::new(public_auth_state
),
146 .set_pkce_challenge(private_auth_state
.pkce_challenge());
148 request
= request
.set_display(CoreAuthDisplay
::Page
);
150 request
= request
.add_prompt(CoreAuthPrompt
::Login
);
152 if let Some(ref scopes
) = self.config
.scopes
{
153 for scope
in scopes
.clone() {
154 request
= request
.add_scope(Scope
::new(scope
));
158 let (authorize_url
, _csrf_state
, _nonce
) = request
.url();
160 Ok(authorize_url
.to_string())
163 pub fn verify_public_auth_state(
166 ) -> Result
<(String
, PrivateAuthState
), Error
> {
167 verify_public_auth_state(Path
::new(state_dir
), state
)
170 pub fn verify_authorization_code(
173 private_auth_state
: &PrivateAuthState
,
174 ) -> Result
<(CoreIdTokenClaims
, GenericUserInfoClaims
), Error
> {
176 let code
= AuthorizationCode
::new(code
.to_string());
177 // Exchange the code with a token.
178 let token_response
= self.client
180 .set_pkce_verifier(private_auth_state
.pkce_verifier())
181 .request(http_client
)
182 .map_err(|err
| format_err
!("Failed to contact token endpoint: {}", err
))?
;
184 let id_token_verifier
: CoreIdTokenVerifier
= self.client
.id_token_verifier();
185 let id_token_claims
: &CoreIdTokenClaims
= token_response
188 .expect("Server did not return an ID token")
189 .claims(&id_token_verifier
, &private_auth_state
.nonce
)
190 .map_err(|err
| format_err
!("Failed to verify ID token: {}", err
))?
;
192 let userinfo_claims
: GenericUserInfoClaims
= self.client
193 .user_info(token_response
.access_token().to_owned(), None
)?
194 .request(http_client
)
195 .map_err(|err
| format_err
!("Failed to contact userinfo endpoint: {}", err
))?
;
197 Ok((id_token_claims
.clone(), userinfo_claims
))