]>
git.proxmox.com Git - proxmox.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
,
37 #[derive(Debug, Deserialize, Serialize, Clone)]
38 pub struct OpenIdConfig
{
39 pub issuer_url
: String
,
40 pub client_id
: String
,
41 #[serde(skip_serializing_if="Option::is_none")]
42 pub client_key
: Option
<String
>,
43 #[serde(skip_serializing_if="Option::is_none")]
44 pub scopes
: Option
<Vec
<String
>>,
47 pub struct OpenIdAuthenticator
{
52 #[derive(Debug, Deserialize, Serialize)]
53 pub struct PublicAuthState
{
54 pub csrf_token
: CsrfToken
,
58 #[derive(Debug, Deserialize, Serialize)]
59 pub struct PrivateAuthState
{
60 pub csrf_token
: CsrfToken
,
62 pub pkce_verifier
: PkceCodeVerifier
,
66 impl PrivateAuthState
{
68 pub fn new() -> Self {
69 let nonce
= Nonce
::new_random();
70 let csrf_token
= CsrfToken
::new_random();
71 let (_pkce_challenge
, pkce_verifier
) = PkceCodeChallenge
::new_random_sha256();
77 ctime
: proxmox_time
::epoch_i64(),
81 pub fn pkce_verifier(&self) -> PkceCodeVerifier
{
82 // Note: PkceCodeVerifier does not impl. clone()
83 PkceCodeVerifier
::new(self.pkce_verifier
.secret().to_string())
86 pub fn pkce_challenge(&self) -> PkceCodeChallenge
{
87 PkceCodeChallenge
::from_code_verifier_sha256(&self.pkce_verifier
)
90 pub fn public_state_string(&self, realm
: String
) -> Result
<String
, Error
> {
91 let pub_state
= PublicAuthState
{
92 csrf_token
: self.csrf_token
.clone(),
95 Ok(serde_json
::to_string(&pub_state
)?
)
99 impl OpenIdAuthenticator
{
101 pub fn discover(config
: &OpenIdConfig
, redirect_url
: &str) -> Result
<Self, Error
> {
103 let client_id
= ClientId
::new(config
.client_id
.clone());
104 let client_key
= config
.client_key
.clone().map(|key
| ClientSecret
::new(key
));
105 let issuer_url
= IssuerUrl
::new(config
.issuer_url
.clone())?
;
107 let provider_metadata
= CoreProviderMetadata
::discover(&issuer_url
, http_client
)?
;
109 let client
= CoreClient
::from_provider_metadata(
113 ).set_redirect_uri(RedirectUrl
::new(String
::from(redirect_url
))?
);
117 config
: config
.clone(),
121 pub fn authorize_url(&self, state_dir
: &str, realm
: &str) -> Result
<String
, Error
> {
123 let private_auth_state
= PrivateAuthState
::new();
124 let public_auth_state
= private_auth_state
.public_state_string(realm
.to_string())?
;
125 let nonce
= private_auth_state
.nonce
.clone();
127 store_auth_state(Path
::new(state_dir
), realm
, &private_auth_state
)?
;
129 // Generate the authorization URL to which we'll redirect the user.
130 let mut request
= self.client
132 CoreAuthenticationFlow
::AuthorizationCode
,
133 || CsrfToken
::new(public_auth_state
),
136 .set_pkce_challenge(private_auth_state
.pkce_challenge());
138 request
= request
.set_display(CoreAuthDisplay
::Page
);
140 request
= request
.add_prompt(CoreAuthPrompt
::Login
);
142 if let Some(ref scopes
) = self.config
.scopes
{
143 for scope
in scopes
.clone() {
144 request
= request
.add_scope(Scope
::new(scope
));
148 let (authorize_url
, _csrf_state
, _nonce
) = request
.url();
150 Ok(authorize_url
.to_string())
153 pub fn verify_public_auth_state(
156 ) -> Result
<(String
, PrivateAuthState
), Error
> {
157 verify_public_auth_state(Path
::new(state_dir
), state
)
160 pub fn verify_authorization_code(
163 private_auth_state
: &PrivateAuthState
,
164 ) -> Result
<CoreIdTokenClaims
, Error
> {
166 let code
= AuthorizationCode
::new(code
.to_string());
167 // Exchange the code with a token.
168 let token_response
= self.client
170 .set_pkce_verifier(private_auth_state
.pkce_verifier())
171 .request(http_client
)
172 .map_err(|err
| format_err
!("Failed to contact token endpoint: {}", err
))?
;
174 let id_token_verifier
: CoreIdTokenVerifier
= self.client
.id_token_verifier();
175 let id_token_claims
: &CoreIdTokenClaims
= token_response
178 .expect("Server did not return an ID token")
179 .claims(&id_token_verifier
, &private_auth_state
.nonce
)
180 .map_err(|err
| format_err
!("Failed to verify ID token: {}", err
))?
;
182 Ok(id_token_claims
.clone())