Include preferred_username and email in OIDC id_token

This commit is contained in:
mdecimus 2024-10-21 15:54:14 +02:00
parent 44d1158aa3
commit c9cd44b35a
4 changed files with 44 additions and 14 deletions

View file

@ -79,10 +79,18 @@ pub struct Userinfo {
}
#[derive(Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct Nonce {
pub struct StandardClaims {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub nonce: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub preferred_username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub email: Option<String>,
}
impl Server {
@ -91,7 +99,7 @@ impl Server {
subject: impl Into<String>,
issuer: impl Into<String>,
audience: impl Into<String>,
nonce: Option<impl Into<String>>,
claims: StandardClaims,
) -> trc::Result<String> {
let now = now() as i64;
@ -101,7 +109,7 @@ impl Server {
key_id: Some("default".into()),
..Default::default()
}),
ClaimsSet::<Nonce> {
ClaimsSet::<StandardClaims> {
registered: RegisteredClaims {
issuer: Some(issuer.into()),
subject: Some(subject.into()),
@ -111,9 +119,7 @@ impl Server {
expiry: Some((now + self.core.oauth.oidc_expiry_id_token as i64).into()),
..Default::default()
},
private: Nonce {
nonce: nonce.map(Into::into),
},
private: claims,
},
)
.into_encoded(&self.core.oauth.oidc_signing_secret)

View file

@ -5,12 +5,16 @@
*/
use common::{
auth::{oauth::GrantType, AccessToken},
auth::{
oauth::{oidc::StandardClaims, GrantType},
AccessToken,
},
Server,
};
use hyper::StatusCode;
use std::future::Future;
use store::write::Bincode;
use trc::AddContext;
use crate::api::{
http::{HttpContext, HttpSessionData, ToHttpResponse},
@ -292,7 +296,22 @@ impl TokenHandler for Server {
None
},
id_token: if with_id_token {
match self.issue_id_token(account_id.to_string(), issuer, client_id, nonce) {
// Obtain access token
let access_token = self
.get_cached_access_token(account_id)
.await
.caused_by(trc::location!())?;
match self.issue_id_token(
account_id.to_string(),
issuer,
client_id,
StandardClaims {
nonce,
preferred_username: access_token.name.clone().into(),
email: access_token.emails.first().cloned(),
},
) {
Ok(id_token) => Some(id_token),
Err(err) => {
trc::error!(err);

View file

@ -11,7 +11,7 @@ use biscuit::{jwk::JWKSet, SingleOrMultiple, JWT};
use bytes::Bytes;
use common::auth::oauth::{
introspect::OAuthIntrospect,
oidc::Nonce,
oidc::StandardClaims,
registration::{ClientRegistrationRequest, ClientRegistrationResponse},
};
use imap_proto::ResponseType;
@ -156,7 +156,7 @@ pub async fn test(params: &mut JMAPTest) {
.is_empty());
// Verify ID token using the JWK set
let id_token = JWT::<Nonce, biscuit::Empty>::new_encoded(&id_token)
let id_token = JWT::<StandardClaims, biscuit::Empty>::new_encoded(&id_token)
.decode_with_jwks(&jwk_set, None)
.unwrap();
let claims = id_token.payload().unwrap();
@ -169,6 +169,11 @@ pub async fn test(params: &mut JMAPTest) {
Some(SingleOrMultiple::Single(client_id.to_string()))
);
assert_eq!(private_claims.nonce, Some("abc1234".to_string()));
assert_eq!(
private_claims.preferred_username,
Some("jdoe@example.com".to_string())
);
assert_eq!(private_claims.email, Some("jdoe@example.com".to_string()));
// Introspect token
let access_introspect: OAuthIntrospect = post_with_auth::<OAuthIntrospect>(

View file

@ -384,12 +384,12 @@ pub async fn jmap_tests() {
mailbox::test(&mut params).await;
delivery::test(&mut params).await;
auth_acl::test(&mut params).await;
auth_limits::test(&mut params).await;
auth_limits::test(&mut params).await;*/
auth_oauth::test(&mut params).await;
event_source::test(&mut params).await;
push_subscription::test(&mut params).await;*/
/*event_source::test(&mut params).await;
push_subscription::test(&mut params).await;
sieve_script::test(&mut params).await;
/*vacation_response::test(&mut params).await;
vacation_response::test(&mut params).await;
email_submission::test(&mut params).await;
websocket::test(&mut params).await;
quota::test(&mut params).await;