OpenID Connect implementation (closes #298)
This commit is contained in:
@@ -7,11 +7,13 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use biscuit::{jwk::JWKSet, SingleOrMultiple, JWT};
|
||||
use bytes::Bytes;
|
||||
use common::auth::oauth::introspect::OAuthIntrospect;
|
||||
use imap_proto::ResponseType;
|
||||
use jmap::auth::oauth::{
|
||||
DeviceAuthResponse, ErrorType, OAuthCodeRequest, OAuthMetadata, TokenResponse,
|
||||
auth::OAuthMetadata, openid::OpenIdMetadata, DeviceAuthResponse, ErrorType, OAuthCodeRequest,
|
||||
TokenResponse,
|
||||
};
|
||||
use jmap_client::{
|
||||
client::{Client, Credentials},
|
||||
@@ -47,20 +49,18 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
|
||||
// Create test account
|
||||
let server = params.server.clone();
|
||||
let john_id = Id::from(
|
||||
server
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.create_test_user(
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
"John Doe",
|
||||
&["jdoe@example.com"],
|
||||
)
|
||||
.await,
|
||||
)
|
||||
.to_string();
|
||||
let john_int_id = server
|
||||
.core
|
||||
.storage
|
||||
.data
|
||||
.create_test_user(
|
||||
"jdoe@example.com",
|
||||
"12345",
|
||||
"John Doe",
|
||||
&["jdoe@example.com"],
|
||||
)
|
||||
.await;
|
||||
let john_id = Id::from(john_int_id).to_string();
|
||||
|
||||
// Build API
|
||||
let api = ManagementApi::new(8899, "jdoe@example.com", "12345");
|
||||
@@ -68,7 +68,13 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
// Obtain OAuth metadata
|
||||
let metadata: OAuthMetadata =
|
||||
get("https://127.0.0.1:8899/.well-known/oauth-authorization-server").await;
|
||||
//println!("OAuth metadata: {:#?}", metadata);
|
||||
let oidc_metadata: OpenIdMetadata =
|
||||
get("https://127.0.0.1:8899/.well-known/openid-configuration").await;
|
||||
let jwk_set: JWKSet<()> = get(&oidc_metadata.jwks_uri).await;
|
||||
|
||||
/*println!("OAuth metadata: {:#?}", metadata);
|
||||
println!("OpenID metadata: {:#?}", oidc_metadata);
|
||||
println!("JWKSet: {:#?}", jwk_set);*/
|
||||
|
||||
// ------------------------
|
||||
// Authorization code flow
|
||||
@@ -114,8 +120,8 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
|
||||
// Obtain token
|
||||
token_params.insert("redirect_uri".to_string(), "https://localhost".to_string());
|
||||
let (token, refresh_token, _) =
|
||||
unwrap_token_response(post(&metadata.token_endpoint, &token_params).await);
|
||||
let (token, refresh_token, id_token) =
|
||||
unwrap_oidc_token_response(post(&metadata.token_endpoint, &token_params).await);
|
||||
|
||||
// Connect to account using token and attempt to search
|
||||
let john_client = Client::new()
|
||||
@@ -132,6 +138,18 @@ pub async fn test(params: &mut JMAPTest) {
|
||||
.ids()
|
||||
.is_empty());
|
||||
|
||||
// Verify ID token using the JWK set
|
||||
let id_token = JWT::<(), biscuit::Empty>::new_encoded(&id_token)
|
||||
.decode_with_jwks(&jwk_set, None)
|
||||
.unwrap();
|
||||
let claims = &id_token.payload().unwrap().registered;
|
||||
assert_eq!(claims.issuer, Some(oidc_metadata.issuer));
|
||||
assert_eq!(claims.subject, Some(john_int_id.to_string()));
|
||||
assert_eq!(
|
||||
claims.audience,
|
||||
Some(SingleOrMultiple::Single("OAuthyMcOAuthFace".to_string()))
|
||||
);
|
||||
|
||||
// Introspect token
|
||||
let access_introspect: OAuthIntrospect = post_with_auth::<OAuthIntrospect>(
|
||||
&metadata.introspection_endpoint,
|
||||
@@ -441,3 +459,17 @@ fn unwrap_token_response(response: TokenResponse) -> (String, Option<String>, u6
|
||||
TokenResponse::Error { error } => panic!("Expected granted, got {:?}", error),
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_oidc_token_response(response: TokenResponse) -> (String, Option<String>, String) {
|
||||
match response {
|
||||
TokenResponse::Granted(granted) => {
|
||||
assert_eq!(granted.token_type, "bearer");
|
||||
(
|
||||
granted.access_token,
|
||||
granted.refresh_token,
|
||||
granted.id_token.unwrap(),
|
||||
)
|
||||
}
|
||||
TokenResponse::Error { error } => panic!("Expected granted, got {:?}", error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +289,47 @@ token = "1s"
|
||||
refresh-token = "3s"
|
||||
refresh-token-renew = "2s"
|
||||
|
||||
[oauth.oidc]
|
||||
signature-key = '''-----BEGIN PRIVATE KEY-----
|
||||
MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQDMXJI1bL3z8gaF
|
||||
Ze/6493VjL+jHkFMP2Pc7fLwRF1fhkuIdYTp69LabzrSEJCRCz0UI2NHqPOgtOta
|
||||
+zRHKAMr7c7Z6uKO0K+aXiQYHw4Y70uSG8CnmNl7kb4OM/CAcoO6fePmvBsyESfn
|
||||
TmkJ5bfHEZQFDQEAoDlDjtjxuwYsAQQVQXuAydi8j8pyTWKAJ1RDgnUT+HbOub7j
|
||||
JrQ7sPe6MPCjXv5N76v9RMHKktfYwRNMlkLkxImQU55+vlvghNztgFlIlJDFfNiy
|
||||
UQPV5FTEZJli9BzMoj1JQK3sZyV8WV0W1zN41QQ+glAAC6+K7iTDPRMINBSwbHyn
|
||||
6Lb9Q6U7AgMBAAECggEAB93qZ5xrhYgEFeoyKO4mUdGsu4qZyJB0zNeWGgdaXCfZ
|
||||
zC4l8zFM+R6osix0EY6lXRtC95+6h9hfFQNa5FWseupDzmIQiEnim1EowjWef87l
|
||||
Eayi0nDRB8TjqZKjR/aLOUhzrPlXHKrKEUk/RDkacCiDklwz9S0LIfLOSXlByBDM
|
||||
/n/eczfX2gUATexMHSeIXs8vN2jpuiVv0r+FPXcRvqdzDZnYSzS8BJ9k6RYXVQ4o
|
||||
NzCbfqgFIpVryB7nHgSTrNX9G7299If8/dXmesXWSFEJvvDSSpcBoINKbfgSlrxd
|
||||
6ubjiotcEIBUSlbaanRrydwShhLHnXyupNAb7tlvyQKBgQDsIipSK4+H9FGl1rAk
|
||||
Gg9DLJ7P/94sidhoq1KYnj/CxwGLoRq22khZEUYZkSvYXDu1Qkj9Avi3TRhw8uol
|
||||
l2SK1VylL5FQvTLKhWB7b2hjrUd5llMRgS3/NIdLhOgDMB7w3UxJnCA/df/Rj+dM
|
||||
WhkyS1f0x3t7XPLwWGurW0nJcwKBgQDdjhrNfabrK7OQvDpAvNJizuwZK9WUL7CD
|
||||
rR0V0MpDGYW12BTEOY6tUK6XZgiRitAXf4EkEI6R0Q0bFzwDDLrg7TvGdTuzNeg/
|
||||
8vm8IlRlOkrdihtHZI4uRB7Ytmz24vzywEBE0p6enA7v4oniscUks/KKmDGr0V90
|
||||
yT9gIVrjGQKBgQCjnWC5otlHGLDiOgm+WhgtMWOxN9dYAQNkMyF+Alinu4CEoVKD
|
||||
VGhA3sk1ufMpbW8pvw4X0dFIITFIQeift3DBCemxw23rBc2FqjkaDi3EszINO22/
|
||||
eUTHyjvcxfCFFPi7aHsNnhJyJm7lY9Kegudmg/Ij93zGE7d5darVBuHvpQKBgBBY
|
||||
YovUgFMLR1UfPeD2zUKy52I4BKrJFemxBNtOKw3mPSIcTfPoFymcMTVENs+eARoq
|
||||
svlZK1uAo8ni3e+Pqd3cQrOyhHQFPxwwrdH+amGJemp7vOV4erDZH7l3Q/S27Fhw
|
||||
bI1nSIKFGukBupB58wRxLiyha9C0QqmYC0/pRg5JAn8Rbj5tP26oVCXjZEfWJL8J
|
||||
axxSxsGA4Vol6i6LYnVgZG+1ez2rP8vUORo1lRzmdeP4o1BSJf9TPwXkuppE5J+t
|
||||
UZVKtYGlEn1RqwGNd8I9TiWvU84rcY9nsxlDR86xwKRWFvYqVOiGYtzRyewYRdjU
|
||||
rTs9aqB3v1+OVxGxR6Na
|
||||
-----END PRIVATE KEY-----
|
||||
'''
|
||||
signature-algorithm = "RS256"
|
||||
|
||||
[oauth.oidc-ignore]
|
||||
signature-key = '''-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQggybcqc86ulFFiOon
|
||||
WiYrLO4z8/kmkqvA7wGElBok9IqhRANCAAQxZK68FnQtHC0eyh8CA05xRIvxhVHn
|
||||
0ymka6XBh9aFtW4wfeoKhTkSKjHc/zjh9Rr2dr3kvmYe80fMGhW4ycGA
|
||||
-----END PRIVATE KEY-----
|
||||
'''
|
||||
signature-algorithm = "ES256"
|
||||
|
||||
[session.extensions]
|
||||
expn = true
|
||||
vrfy = true
|
||||
|
||||
Reference in New Issue
Block a user