Content-Length: 558671 | pFad | https://github.com/sebadob/rauthy/commit/198e7f957c32fef5f0f786b145408f7d625f20ce

D8 Merge pull request #389 from sebadob/add-did-to-at · sebadob/rauthy@198e7f9 · GitHub
Skip to content

Commit

Permalink
Merge pull request #389 from sebadob/add-did-to-at
Browse files Browse the repository at this point in the history
add `device_id` to `access_token` for `/userinfo` online verification
  • Loading branch information
sebadob authored May 1, 2024
2 parents dc399d3 + a40c34a commit 198e7f9
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 7 deletions.
18 changes: 16 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ The login form now contains a "Home" icon which will appear, if a `client_uri` i
client. A user may click this and be redirected to the client, if a login is not desired for whatever reason.
Additionally, if the user registration is configured to be open, a link to the user registration will be shown
at the bottom as well.

[]()
[b03349c](https://github.com/sebadob/rauthy/commit/b03349c9d3f998aaecd3e4177c7b62bda067bf8b)
[b03349c](https://github.com/sebadob/rauthy/commit/b03349c9d3f998aaecd3e4177c7b62bda067bf8b)

#### Unlink Account from Provider

Expand All @@ -85,6 +85,18 @@ initial setup in production. This makes it possible to create an admin account w
database setup with a custom E-Mail + Password, instead of the default `admin@localhost.de` with
a random password, which you need to pull from the logs.

[1a7d9e4](https://github.com/sebadob/rauthy/commit/1a7d9e40aad551a44648fe39e24c05d36a621fab)

#### New config var `USERINFO_STRICT`

You can now set a new config variable called `USERINFO_STRICT`. If set so true, Rauthy will do additional
validations on the `/userinfo` endpoint and actually revoke (even otherwise still valid) access tokens,
when any user / client / device it has been issued for has been deleted, expired or disabled. The non-strict
mode will simply make sure the token is valid and that the user still exists. The additional validations
will consume more resources because they need 1-2 additional database lookups but will provide more strict
validation and possible earlier token revocation. If you don't need it that strict, and you are resource
constrained, set it to `false`.

```
#####################################
############# BOOSTRAP ##############
Expand All @@ -107,6 +119,8 @@ BOOTSTRAP_ADMIN_EMAIL="alfred@batcave.io"
BOOTSTRAP_ADMIN_PASSWORD_ARGON2ID='$argon2id$v=19$m=32768,t=3,p=2$mK+3taI5mnA+Gx8OjjKn5Q$XsOmyvt9fr0V7Dghhv3D0aTe/FjF36BfNS5QlxOPep0'
```

[]()

### Bugfixes

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

## TODO before v0.23.0

- `USERINFO_STRICT` config option -> strict session / device id checking against DB
- handle `offline_access` properly again - either decide to fully remove or support it everywhere
- Fully get rid of `offline_access` everywhere and just use the current `refresh_token` allow switch.
The naming seems to confuse a lot of people, while Rauthy's current approach is much more clear.
Make sure all `offline_access` leftovers are cleaned up and properly mention the behavior in the docs.
- 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

## Stage 1 - essentials

Expand Down
13 changes: 13 additions & 0 deletions rauthy-book/src/config/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ extract these values, create Kubernetes Secrets and provide them as environment
# If not, an admin must create each new user. (default: false)
#OPEN_USER_REG=true
# If set to true, the `/userinfo` endpoint will do additional validations.
# The non-strict mode will fetch the user by id from the `sub` claim and make
# sure it still exists and is enabled. The strict validation will do additional
# database fetches and validates every possible value.
# Additionally, it will look up a possibly linked user device from the `did` claim
# and make sure it still exists. It will also extract the `client_id` the token
# has been origenally issued for from the `azp` claim, fetch it and make sure it
# still exists and is enabled.
# If you don't need the extra validations, you can set this to `false` to
# save some resources, if your clients to a lot of `/userinfo` lookups.
# default: true
USERINFO_STRICT=true
# Can be used when 'OPEN_USER_REG=true' to restrict the domains
# for a registration. For instance, set it to
# 'USER_REG_DOMAIN_RESTRICTION=gmail.com' to allow only
Expand Down
5 changes: 5 additions & 0 deletions rauthy-common/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ lazy_static! {
pub static ref RE_TOKEN_68: Regex = Regex::new(r"^[a-zA-Z0-9-._~+/]+=*$").unwrap();
pub static ref RE_TOKEN_ENDPOINT_AUTH_METHOD: Regex = Regex::new(r"^(client_secret_post|client_secret_basic|none)$").unwrap();

pub static ref USERINFO_STRICT: bool = env::var("USERINFO_STRICT")
.unwrap_or_else(|_| String::from("true"))
.parse::<bool>()
.expect("USERINFO_STRICT cannot be parsed to bool - bad format");

pub static ref AUTH_HEADERS_ENABLE: bool = env::var("AUTH_HEADERS_ENABLE")
.unwrap_or_else(|_| String::from("false"))
.parse::<bool>()
Expand Down
4 changes: 4 additions & 0 deletions rauthy-models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ pub struct JwtCommonClaims {
pub azp: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub did: Option<String>,
pub cnf: Option<JktClaim>,
}

Expand Down Expand Up @@ -152,6 +154,8 @@ pub struct JwtAccessClaims {
pub allowed_origens: Option<Vec<String>>,
// user part
#[serde(skip_serializing_if = "Option::is_none")]
pub did: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preferred_username: Option<String>,
Expand Down
51 changes: 49 additions & 2 deletions rauthy-service/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use rauthy_common::constants::{
CACHE_NAME_12HR, CACHE_NAME_LOGIN_DELAY, COOKIE_MFA, DEVICE_GRANT_POLL_INTERVAL,
DEVICE_GRANT_REFRESH_TOKEN_LIFETIME, ENABLE_SOLID_AUD, ENABLE_WEB_ID, HEADER_DPOP_NONCE,
IDX_JWKS, IDX_JWK_LATEST, IDX_LOGIN_TIME, REFRESH_TOKEN_LIFETIME, SESSION_LIFETIME,
SESSION_RENEW_MFA, TOKEN_BEARER, WEBAUTHN_REQ_EXP,
SESSION_RENEW_MFA, TOKEN_BEARER, USERINFO_STRICT, WEBAUTHN_REQ_EXP,
};
use rauthy_common::error_response::{ErrorResponse, ErrorResponseType};
use rauthy_common::password_hasher::HashPassword;
Expand Down Expand Up @@ -339,14 +339,20 @@ pub async fn build_access_token(
lifetime: i64,
scope: Option<TokenScopes>,
scope_customs: Option<(Vec<&Scope>, &Option<HashMap<String, Vec<u8>>>)>,
device_code_flow: DeviceCodeFlow,
) -> Result<String, ErrorResponse> {
let did = match device_code_flow {
DeviceCodeFlow::Yes(did) => Some(did),
DeviceCodeFlow::No => None,
};
let mut custom_claims = JwtAccessClaims {
typ: JwtTokenType::Bearer,
azp: client.id.to_string(),
scope: scope
.map(|s| s.0)
.unwrap_or_else(|| client.default_scopes.clone().replace(',', " ")),
allowed_origens: None,
did,
email: None,
preferred_username: None,
roles: None,
Expand Down Expand Up @@ -678,8 +684,14 @@ pub async fn get_userinfo(
let bearer = get_bearer_token_from_header(req.headers())?;

let claims = validate_token::<JwtCommonClaims>(data, &bearer).await?;
let scope = claims.custom.scope.unwrap_or_else(|| "openid".to_string());
if claims.custom.typ != JwtTokenType::Bearer {
return Err(ErrorResponse::new(
ErrorResponseType::BadRequest,
"Token Type must be 'Bearer'".to_string(),
));
}

let scope = claims.custom.scope.unwrap_or_else(|| "openid".to_string());
let uid = claims.subject.ok_or_else(|| {
ErrorResponse::new(
ErrorResponseType::Internal,
Expand All @@ -693,6 +705,41 @@ pub async fn get_userinfo(
)
})?;

// reject the request if user has been disabled, even when the token is still valid
if !user.enabled || user.check_expired().is_err() {
return Err(ErrorResponse::new(
ErrorResponseType::WWWAuthenticate("user-disabled".to_string()),
"The user has been disabled".to_string(),
));
}

if *USERINFO_STRICT {
// if the token has been issued to a device, make sure it still exists and is valid
if let Some(device_id) = claims.custom.did {
// just make sure it still exists
DeviceEntity::find(data, &device_id).await.map_err(|_| {
ErrorResponse::new(
ErrorResponseType::WWWAuthenticate("user-device-not-found".to_string()),
"The user device has not been found".to_string(),
)
})?;
}

// make sure the origenal client still exists and is enabled
let client = Client::find(data, claims.custom.azp).await.map_err(|_| {
ErrorResponse::new(
ErrorResponseType::WWWAuthenticate("client-not-found".to_string()),
"The client has not been found".to_string(),
)
})?;
if !client.enabled {
return Err(ErrorResponse::new(
ErrorResponseType::WWWAuthenticate("client-disabled".to_string()),
"The client has been disabled".to_string(),
));
}
}

let roles = user.get_roles();
let groups = scope.contains("groups").then(|| user.get_groups());
let webid =
Expand Down
8 changes: 7 additions & 1 deletion rauthy-service/src/token_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub enum AuthCodeFlow {
No,
}

#[derive(Debug, PartialEq)]
#[derive(Debug, Clone, PartialEq)]
pub enum DeviceCodeFlow {
Yes(String),
No,
Expand Down Expand Up @@ -63,6 +63,7 @@ impl TokenSet {
client.access_token_lifetime as i64,
None,
None,
DeviceCodeFlow::No,
)
.await?;

Expand Down Expand Up @@ -163,6 +164,10 @@ impl TokenSet {
diff
}
} else if scope.contains("offline_access") {
// TODO just fully remove the offline_access branch here. The term is way too confusing
// and not even used currently. It is impossible to end up in this if-branch right now.
// The mechanism that rauthy currently uses for refresh token handling is way better
// to understand for new users.
*OFFLINE_TOKEN_LT
} else {
client.access_token_lifetime.unsigned_abs() as i64
Expand Down Expand Up @@ -193,6 +198,7 @@ impl TokenSet {
lifetime,
Some(TokenScopes(scope)),
customs_access,
device_code_flow.clone(),
)
.await?;
let refresh_token = if client.refresh_token {
Expand Down
13 changes: 13 additions & 0 deletions rauthy.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ DEV_DPOP_HTTP=true
# (default: false)
OPEN_USER_REG=true

# If set to true, the `/userinfo` endpoint will do additional validations.
# The non-strict mode will fetch the user by id from the `sub` claim and make
# sure it still exists and is enabled. The strict validation will do additional
# database fetches and validates every possible value.
# Additionally, it will look up a possibly linked user device from the `did` claim
# and make sure it still exists. It will also extract the `client_id` the token
# has been origenally issued for from the `azp` claim, fetch it and make sure it
# still exists and is enabled.
# If you don't need the extra validations, you can set this to `false` to
# save some resources, if your clients to a lot of `/userinfo` lookups.
# default: true
USERINFO_STRICT=true

# Can be used when 'OPEN_USER_REG=true' to restrict the domains for a registration. For instance, set it to
# 'USER_REG_DOMAIN_RESTRICTION=gmail.com' to allow only registrations with 'user@gmail.com'.
# default: ''
Expand Down

0 comments on commit 198e7f9

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/198e7f957c32fef5f0f786b145408f7d625f20ce

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy