Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rocket): add auth guard for rocket oauth2.0 introspection (#319)
This adds rocket as a possible feature to have a OAuth2.0 Token introspection route guard. The guard does check the incoming token and introspects it against a given ZITADEL instance. A new feature "rocket" is used to enable all rocket specific stuff. BREAKING CHANGE: This removes `api` as a default feature. To migrate, just add `api` to the used features of the crate.
- Loading branch information
Showing
23 changed files
with
1,183 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
{ | ||
"id": "31a70203-3772-4e6a-8cf2-df399183a921", | ||
"importStreams": [], | ||
"authConfigs": [ | ||
{ | ||
"name": "ZITADEL", | ||
"providerName": "oidc", | ||
"id": "483e44fe-3c97-4e75-9ec8-0237764b6a3b", | ||
"options": { | ||
"redirectUri": "http://localhost/", | ||
"clientId": "194289529828475137@zitadel_rust_test", | ||
"scope": "openid profile email", | ||
"issuer": "https://zitadel-libraries-l8boqa.zitadel.cloud", | ||
"clientAuthMethod": "none", | ||
"grantType": "authorizationCode" | ||
} | ||
} | ||
], | ||
"certificates": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"settings": [ | ||
{ | ||
"options": { | ||
"rest": { | ||
"endpoint": "http://127.0.0.1:8000/", | ||
"disableServerCertificateValidation": true | ||
} | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"details": { | ||
"path": "/authed", | ||
"method": "GET", | ||
"headers": [] | ||
}, | ||
"requests": [ | ||
{ | ||
"contentType": "none" | ||
} | ||
], | ||
"authId": "483e44fe-3c97-4e75-9ec8-0237764b6a3b", | ||
"operationType": "unary", | ||
"invokerName": "rest", | ||
"typeHint": "GET" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"details": { | ||
"path": "/unauthed", | ||
"method": "GET", | ||
"headers": [] | ||
}, | ||
"requests": [ | ||
{ | ||
"contentType": "none" | ||
} | ||
], | ||
"operationType": "unary", | ||
"invokerName": "rest", | ||
"typeHint": "GET" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use zitadel::rocket::introspection::{IntrospectedUser, IntrospectionConfigBuilder}; | ||
|
||
#[rocket::get("/unauthed")] | ||
fn unauthed() -> &'static str { | ||
"Hello Unauthorized User" | ||
} | ||
|
||
#[rocket::get("/authed")] | ||
fn authed(user: &IntrospectedUser) -> String { | ||
format!( | ||
"Hello Authorized {:?} with id {}", | ||
user.username, user.user_id | ||
) | ||
} | ||
|
||
#[rocket::launch] | ||
async fn rocket() -> _ { | ||
rocket::build() | ||
.mount("/", rocket::routes![unauthed, authed]) | ||
.manage( | ||
IntrospectionConfigBuilder::new("https://zitadel-libraries-l8boqa.zitadel.cloud") | ||
.with_basic_auth( | ||
"194339055499018497@zitadel_rust_test", | ||
"Ip56oGzxKL1rJ8JaleUVKL7qUlpZ1tqHQYRSd6JE1mTlTJ3pDkDzoObHdZsOg88B", | ||
) | ||
.build() | ||
.await | ||
.unwrap(), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
use zitadel::{ | ||
credentials::Application, | ||
rocket::introspection::{IntrospectedUser, IntrospectionConfigBuilder}, | ||
}; | ||
|
||
const APPLICATION: &str = r#" | ||
{ | ||
"type": "application", | ||
"keyId": "181963758610940161", | ||
"key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAwT2YZJytkkZ1DDM3dcu1OA8YPzHu6XR8HotdMNRnV75GhOT4\nB7zDtdtoP8w/1NHHPEJ859e0kYhrrnKikOKLS6fS1KRsmqR5ZvTq8SlZ2mq3RcX2\nebZx5dQt36INij/WXdsBmjM/yfWvqqWBSb0L/186DaWwmmIxoXWe873vxRmlzblg\nGd8Nu07s9YTREbGPbtFVHEUM6xI4oIe8HJ0e1+JBkiGqk31Cogo0FoAxrOAg0Sf4\n5XiUMYIjzqh8673F9SC4IpVxG22mpFk3vDFuAITaStWYbiH2hPJNKWyX9HDCZb1D\nDqa3wZBDiLqWxh22hNZ6ZIe+3UoSGWsPBH+E1wIDAQABAoIBAD2v5QsRPRN57HmF\njAnNir8nimz6CrN53Pl/MbOZypenBSn9UfReXPeb3+6lzCarBPgGnYsBQAJJU16v\n95daym7PVy1Mg+Ll6F9mhe2Qbr+b23+pj2IRTNC6aB6Aw+PDNzJk7GEGRTG6fWZz\nSQ96Cu9tvcGHiBXwjLlnK+PRWU5IsCiLsjT4xBXsMLMw3YOdMK5z58sqr+SnNEyq\nRHoEvi9aC94WrargVB45Yx+81YNW8uQ5rMDmYaJC5a7ENz522SlAuf4T+fAGJ/HE\n/qbZGD4YwlLqAFDgewQ+5tEWEus3zgY2MIR7vN2zXU1Ptk+mQkXZl/Pxdp7q1xU+\nvr/kcykCgYEAy7MiIAzc1ctQDvkk3HiespzdQ/sC7+CGsBzkyubRc9Oq/YR7GfVK\nGTuDEDlWwx92VAvJGDWRa3T426YDyqiPj66uo836sgL15Uigg5afZun2bqGC78le\nBhSy9b+0YDHPa87GxtKt9UmMoB6WdmoPzOkLEEGS7eesmk2DDgY+QSUCgYEA8tr/\n3PawigL1cxuFpcO1lH6XUspGeAo5yB8FXvfW5g50e37LgooIvOFgUlYuchxwr6uh\nW+CUAWmm4farsgvMBMPYw+PbkCTi/xemiiDmMHUYd7sJkTl0JXApq3pZsNMg4Fw/\n29RynmcG8TGe2dkwrWp1aBYjvIHwEHuNHHTTA0sCgYBtSUFAwsXkaj0cm2y8YHZ8\nS46mv1AXFHYOnKHffjDXnLN7ao2FIsXLfdNWa/zxmLqqYtxUAcFwToSJi6szGnZT\nVxvZRFSBFveIOQvtLW1+EH4nYr3WGko4pvhQwrZqea7YH0skNrogBILPEToWc9bg\nUBOgeB31R7uh2X47kvvphQKBgQDWc60dYnniZVp5mwQZrQjbaC4YXaZ8ugrsPPhx\nNEoAPSN/KihrzZiJsjtsec3p1lNrzRNgHqCT3sgPIdPcFa7DRm5UDRIF54zL1gaq\nUwLyJ3TDxdZc928o4DLryc8J5mZRuSRq6t+MIU5wDnFHzhK+EBQ9Jc/I1rU22ONz\nDXaIoQKBgH14Apggo0o4Eo+OnEBRFbbDulaOfVLPTK9rktikbwO1vzDch8kdcwCU\nsvtRXHjDQL93Ih/8S9aDJZoSDulwr3VUsuDiDEb4jfYmP2sbNO4nIJt+SBMhVOXV\nt7E/uWK28X0GL/bIUzSMMgTfdjhXEtJW+s6hQU1fG+9U1qVTQ2R/\n-----END RSA PRIVATE KEY-----\n", | ||
"appId": "181963751145079041", | ||
"clientId": "181963751145144577@zitadel_rust_test" | ||
}"#; | ||
|
||
#[rocket::get("/unauthed")] | ||
fn unauthed() -> &'static str { | ||
"Hello Unauthorized User" | ||
} | ||
|
||
#[rocket::get("/authed")] | ||
fn authed(user: &IntrospectedUser) -> String { | ||
format!( | ||
"Hello Authorized {:?} with id {}", | ||
user.username, user.user_id | ||
) | ||
} | ||
|
||
#[rocket::launch] | ||
async fn rocket() -> _ { | ||
rocket::build() | ||
.mount("/", rocket::routes![unauthed, authed]) | ||
.manage( | ||
IntrospectionConfigBuilder::new("https://zitadel-libraries-l8boqa.zitadel.cloud") | ||
.with_jwt_profile(Application::load_from_json(APPLICATION).unwrap()) | ||
.build() | ||
.await | ||
.unwrap(), | ||
) | ||
} |
Submodule googleapis
updated
489 files
Submodule grpc-gateway
updated
78 files
Submodule protoc-gen-validate
updated
from 0bd377 to 0c0491
Submodule zitadel
updated
from 5c8cfa to add232
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
use custom_error::custom_error; | ||
use openidconnect::reqwest::async_http_client; | ||
use openidconnect::{ | ||
core::{ | ||
CoreAuthDisplay, CoreClaimName, CoreClaimType, CoreClientAuthMethod, CoreGrantType, | ||
CoreJsonWebKey, CoreJsonWebKeyType, CoreJsonWebKeyUse, CoreJweContentEncryptionAlgorithm, | ||
CoreJweKeyManagementAlgorithm, CoreJwsSigningAlgorithm, CoreResponseMode, CoreResponseType, | ||
CoreSubjectIdentifierType, | ||
}, | ||
url, AdditionalProviderMetadata, IntrospectionUrl, IssuerUrl, ProviderMetadata, RevocationUrl, | ||
}; | ||
use serde::{Deserialize, Serialize}; | ||
|
||
custom_error! { | ||
/// Error type for discovery related errors. | ||
pub DiscoveryError | ||
IssuerUrl{source: url::ParseError} = "could not parse issuer url: {source}", | ||
DiscoveryDocument = "could not discover OIDC document", | ||
} | ||
|
||
/// Fetch the well-known [OIDC Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) | ||
/// document of a given `authority`. "Authority" is a synonym for "Issuer" and vice versa. | ||
/// The discovery document contains information about various OIDC endpoints of the ZITADEL | ||
/// instance. Note that the authority (issuer) must not contain the well-known url part | ||
/// (`/.well-known/openid-configuration`). | ||
/// | ||
/// The returned [metadata](ZitadelProviderMetadata) contains the parsed information of the | ||
/// well-known OIDC configuration. | ||
/// | ||
/// ### Errors | ||
/// | ||
/// This method may fail if: | ||
/// - The authority url cannot be parsed correctly | ||
/// - The discovery call throws any kind of error | ||
/// | ||
/// ### Example | ||
/// | ||
/// #### Fetch the discovery document of the "ZITADEL Libraries" - Test Instance | ||
/// | ||
/// ``` | ||
/// # #[tokio::main] | ||
/// # async fn main() -> Result<(), Box<dyn std::error::Error>>{ | ||
/// use zitadel::oidc::discovery::discover; | ||
/// let authority = "https://zitadel-libraries-l8boqa.zitadel.cloud"; | ||
/// let metadata = discover(authority).await?; | ||
/// println!("{:?}", metadata.token_endpoint()); | ||
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
pub async fn discover(authority: &str) -> Result<ZitadelProviderMetadata, DiscoveryError> { | ||
let issuer = IssuerUrl::new(authority.to_string()) | ||
.map_err(|source| DiscoveryError::IssuerUrl { source })?; | ||
ZitadelProviderMetadata::discover_async(issuer, async_http_client) | ||
.await | ||
.map_err(|_| DiscoveryError::DiscoveryDocument) | ||
} | ||
|
||
/// Definition of additional metadata that is not present in the | ||
/// standard metadata of the openidconnect crate. | ||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub struct ZitadelAdditionalMetadata { | ||
pub introspection_endpoint: Option<IntrospectionUrl>, | ||
pub revocation_endpoint: Option<RevocationUrl>, | ||
} | ||
|
||
impl AdditionalProviderMetadata for ZitadelAdditionalMetadata {} | ||
|
||
/// Type to the ZITADEL provider metadata. Essentially combines | ||
/// the [ZitadelAdditionalMetadata] with the openid provider | ||
/// metadata information. | ||
pub type ZitadelProviderMetadata = ProviderMetadata< | ||
ZitadelAdditionalMetadata, | ||
CoreAuthDisplay, | ||
CoreClientAuthMethod, | ||
CoreClaimName, | ||
CoreClaimType, | ||
CoreGrantType, | ||
CoreJweContentEncryptionAlgorithm, | ||
CoreJweKeyManagementAlgorithm, | ||
CoreJwsSigningAlgorithm, | ||
CoreJsonWebKeyType, | ||
CoreJsonWebKeyUse, | ||
CoreJsonWebKey, | ||
CoreResponseMode, | ||
CoreResponseType, | ||
CoreSubjectIdentifierType, | ||
>; | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
#![allow(clippy::all)] | ||
|
||
use super::*; | ||
|
||
const ZITADEL_URL: &str = "https://zitadel-libraries-l8boqa.zitadel.cloud"; | ||
|
||
#[tokio::test] | ||
async fn discovery_fails_with_invalid_url() { | ||
let result = discover("foobar").await; | ||
|
||
assert!(result.is_err()); | ||
assert!(matches!( | ||
result.unwrap_err(), | ||
DiscoveryError::IssuerUrl { .. } | ||
)); | ||
} | ||
|
||
#[tokio::test] | ||
async fn discovery_fails_with_invalid_discovery() { | ||
let result = discover("https://smartive.ch").await; | ||
|
||
assert!(result.is_err()); | ||
assert!(matches!( | ||
result.unwrap_err(), | ||
DiscoveryError::DiscoveryDocument | ||
)); | ||
} | ||
|
||
#[tokio::test] | ||
async fn discovery_succeeds() { | ||
let result = discover(ZITADEL_URL).await.unwrap(); | ||
|
||
assert_eq!( | ||
result.token_endpoint().unwrap().to_string(), | ||
"https://zitadel-libraries-l8boqa.zitadel.cloud/oauth/v2/token".to_string() | ||
); | ||
} | ||
} |
Oops, something went wrong.