Content-Length: 497425 | pFad | https://github.com/sebadob/rauthy/commit/d506865898e61fce45e5cf4c754ad4300bd37161

70 Merge pull request #392 from sebadob/impl-at_hash · sebadob/rauthy@d506865 · GitHub
Skip to content

Commit

Permalink
Merge pull request #392 from sebadob/impl-at_hash
Browse files Browse the repository at this point in the history
feat: add `at_hash` to the `id_token`
  • Loading branch information
sebadob authored May 1, 2024
2 parents 71db7fe + 9765bbb commit d506865
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 16 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ The "new" mechanism Rauthy uses with the switch in the Admin UI to issue / allow
is much more clear, since the `offline_access` scope produces a lot of confusion for people new to OIDC.
From the name, it simply makes no sense that you need to activate `offline_access` to get a refresh token.
Having an option named "allow refresh tokens" is just so much better.
[71db7fe](https://github.com/sebadob/rauthy/commit/71db7fef18568a599f30cae6e494bba40cb33e7d)

### Features

Expand Down Expand Up @@ -133,6 +134,14 @@ constrained, set it to `false`.

[198e7f9](https://github.com/sebadob/rauthy/commit/198e7f957c32fef5f0f786b145408f7d625f20ce)

#### `at_hash` in `id_token`

The Rauthy `id_token` now contains the access token hash `at_hash` claim. This is needed for additional
downstream validation, if a client provides both tokens and they are not coming from Rauthy directly.
With the additional validation of the `at_hash` claim, clients can be 100% sure, that a given `id_token`
belongs to a specific `access_token` and has not been swapped out.
[]()

### Bugfixes

- The button for requesting a password reset from inside a federated account view has been
Expand Down
11 changes: 8 additions & 3 deletions dev_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@

## TODO before v0.23.0

- add `at_hash` claim to the ID token
- fix button label misplacement on chrome + ugly tab bar
- sessions view needs pagination
- `refresh_token` handler in rauthy-client for `device_code` grant flow
- check possibility of `no_std` for rauthy-client + device_code
- check out the possibility to include SCIM

## Stage 1 - essentials

[x] finished

## Stage 2 - features - do before v1.0.0

- prettify the UI
- update the book with all the new features
- benchmarks and performance tuning
- maybe get a nicer logo
Expand All @@ -25,6 +24,12 @@
- all the `get_`s on the `Client` will probably be good with returning slices instead of real Strings
-> less memory allocations

### `rauthy-client` TODO's

- check possibility of `no_std` with `device_code` flow -> embedded devices
- automatic `refresh_token` handler for `device_code` grant flow
- when implementing userinfo lookup, add an fn to validate the `at_hash` as well

## Stage 3 - Possible nice to haves

- respect `display=popup` and / or `display=touch` on `/authorize`
Expand Down
1 change: 1 addition & 0 deletions rauthy-client/src/token_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub struct JwtIdClaims {
pub typ: JwtTokenType,
pub amr: Vec<String>,
pub auth_time: i64,
pub at_hash: Option<String>,
pub preferred_username: String,
pub email: Option<String>,
pub email_verified: Option<bool>,
Expand Down
1 change: 1 addition & 0 deletions rauthy-models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ pub struct JwtIdClaims {
pub typ: JwtTokenType,
pub amr: Vec<String>,
pub auth_time: i64,
pub at_hash: String,
pub preferred_username: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
Expand Down
7 changes: 5 additions & 2 deletions rauthy-service/src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::token_set::{
AuthCodeFlow, DeviceCodeFlow, DpopFingerprint, TokenNonce, TokenScopes, TokenSet,
AtHash, AuthCodeFlow, DeviceCodeFlow, DpopFingerprint, TokenNonce, TokenScopes, TokenSet,
};
use actix_web::http::header;
use actix_web::http::header::{HeaderMap, HeaderName, HeaderValue};
Expand Down Expand Up @@ -330,7 +330,8 @@ pub async fn authorize_refresh(
}

//github.com/ Builds the access token for a user after all validation has been successful
#[allow(clippy::type_complexity)]
// too many arguments is not an issue - params cannot be mistaken because of enum wrappers
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
pub async fn build_access_token(
user: Option<&User>,
data: &web::Data<AppState>,
Expand Down Expand Up @@ -420,6 +421,7 @@ pub async fn build_id_token(
data: &web::Data<AppState>,
client: &Client,
dpop_fingerprint: Option<DpopFingerprint>,
at_hash: AtHash,
lifetime: i64,
nonce: Option<TokenNonce>,
scope: &str,
Expand Down Expand Up @@ -463,6 +465,7 @@ pub async fn build_id_token(
typ: JwtTokenType::Id,
amr: vec![amr],
auth_time,
at_hash: at_hash.0,
preferred_username: user.email.clone(),
email: None,
email_verified: None,
Expand Down
91 changes: 80 additions & 11 deletions rauthy-service/src/token_set.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,60 @@
use crate::auth;
use actix_web::web;
use rauthy_common::error_response::{ErrorResponse, ErrorResponseType};
use rauthy_common::utils::base64_url_no_pad_encode;
use rauthy_models::app_state::AppState;
use rauthy_models::entity::clients::Client;
use rauthy_models::entity::scopes::Scope;
use rauthy_models::entity::user_attr::UserAttrValueEntity;
use rauthy_models::entity::users::User;
use rauthy_models::JwtTokenType;
use ring::digest;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use time::OffsetDateTime;
use utoipa::ToSchema;

pub enum AtHashAlg {
Sha256,
Sha384,
Sha512,
}

impl TryFrom<&str> for AtHashAlg {
type Error = ErrorResponse;

fn try_from(value: &str) -> Result<Self, Self::Error> {
let slf = match value {
"RS256" => Self::Sha256,
"RS384" => Self::Sha384,
"RS512" => Self::Sha512,
"EdDSA" => Self::Sha512,
_ => {
return Err(ErrorResponse::new(
ErrorResponseType::Internal,
"Cannot build AtHashAlg from value".to_string(),
));
}
};
Ok(slf)
}
}

pub struct AtHash(pub String);

impl AtHash {
pub fn build(access_token: &[u8], alg: AtHashAlg) -> Self {
let hash = match alg {
AtHashAlg::Sha256 => digest::digest(&digest::SHA256, access_token),
AtHashAlg::Sha384 => digest::digest(&digest::SHA384, access_token),
AtHashAlg::Sha512 => digest::digest(&digest::SHA512, access_token),
};
let bytes = hash.as_ref();
let (left_bits, _) = bytes.split_at(bytes.len() / 2);
Self(base64_url_no_pad_encode(left_bits))
}
}

#[derive(Debug, PartialEq)]
pub enum AuthCodeFlow {
Yes,
Expand Down Expand Up @@ -171,27 +214,33 @@ impl TokenSet {
} else {
JwtTokenType::Bearer
};
let id_token = auth::build_id_token(
user,
let access_token = auth::build_access_token(
Some(user),
data,
client,
dpop_fingerprint.clone(),
lifetime,
nonce,
&scope,
customs_id,
auth_code_flow,
Some(TokenScopes(scope.clone())),
customs_access,
device_code_flow.clone(),
)
.await?;
let access_token = auth::build_access_token(
Some(user),

let at_hash = AtHash::build(
access_token.as_bytes(),
AtHashAlg::try_from(client.access_token_alg.as_str())?,
);
let id_token = auth::build_id_token(
user,
data,
client,
dpop_fingerprint.clone(),
at_hash,
lifetime,
Some(TokenScopes(scope)),
customs_access,
device_code_flow.clone(),
nonce,
&scope,
customs_id,
auth_code_flow,
)
.await?;
let refresh_token = if client.refresh_token {
Expand Down Expand Up @@ -221,3 +270,23 @@ impl TokenSet {
})
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_at_hash() {
let ref_token =
b"YmJiZTAwYmYtMzgyOC00NzhkLTkyOTItNjJjNDM3MGYzOWIy9sFhvH8K_x8UIHj1osisS57f5DduL";

let sha256 = AtHash::build(ref_token, AtHashAlg::Sha256);
assert_eq!(&sha256.0, "xsZZrUssMXjL3FBlzoSh2g");

let sha384 = AtHash::build(ref_token, AtHashAlg::Sha384);
assert_eq!(&sha384.0, "adt46pcdiB-l6eTNifgoVM-5AIJAxq84");

let sha512 = AtHash::build(ref_token, AtHashAlg::Sha512);
assert_eq!(&sha512.0, "p2LHG4H-8pYDc0hyVOo3iIHvZJUqe9tbj3jESOuXbkY");
}
}

0 comments on commit d506865

Please sign in to comment.








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: https://github.com/sebadob/rauthy/commit/d506865898e61fce45e5cf4c754ad4300bd37161

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy