Add c_hash, at_hash and nonce claims to id_token
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1405,6 +1405,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"serde_with",
|
||||
"serde_yaml",
|
||||
"sha2",
|
||||
"sqlx",
|
||||
"tera",
|
||||
"thiserror",
|
||||
|
||||
@@ -49,12 +49,14 @@ dotenv = "0.15.0"
|
||||
argon2 = { version = "0.3.0", features = ["password-hash"] }
|
||||
password-hash = { version = "0.3.0", features = ["std"] }
|
||||
|
||||
# Crypto and signing stuff
|
||||
# Crypto, hashing and signing stuff
|
||||
rsa = "0.5.0"
|
||||
k256 = "0.9.6"
|
||||
pkcs8 = { version = "0.7.5", features = ["pem"] }
|
||||
elliptic-curve = { version = "0.10.6", features = ["pem"] }
|
||||
chacha20poly1305 = { version = "0.9.0", features = ["std"] }
|
||||
sha2 = "0.9.8"
|
||||
crc = "2.0.0"
|
||||
|
||||
# Various data types and utilities
|
||||
data-encoding = "2.3.2"
|
||||
@@ -66,7 +68,6 @@ rand = "0.8.4"
|
||||
bincode = "1.3.3"
|
||||
headers = "0.3.4"
|
||||
cookie = "0.15.1"
|
||||
crc = "2.0.0"
|
||||
|
||||
oauth2-types = { path = "../oauth2-types", features = ["sqlx_type"] }
|
||||
|
||||
|
||||
@@ -381,20 +381,8 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"88ac8783bd5881c42eafd9cf87a16fe6031f3153fd6a8618e689694584aeb2de": {
|
||||
"query": "\n DELETE FROM oauth2_access_tokens\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"8c21b0b46e74ae5667b82bfe57706a64396683ac4dc29311424008c3f3e94136": {
|
||||
"query": "\n SELECT\n oc.id,\n os.id AS \"oauth2_session_id!\",\n os.client_id AS \"client_id!\",\n os.redirect_uri,\n os.scope AS \"scope!\"\n FROM oauth2_codes oc\n INNER JOIN oauth2_sessions os\n ON os.id = oc.oauth2_session_id\n WHERE oc.code = $1\n ",
|
||||
"886dee6a6f1f426f0e891790bbeffbc222fd75d8da0a107e7de673f1cc445f30": {
|
||||
"query": "\n SELECT\n oc.id,\n os.id AS \"oauth2_session_id!\",\n os.client_id AS \"client_id!\",\n os.redirect_uri,\n os.scope AS \"scope!\",\n os.nonce\n FROM oauth2_codes oc\n INNER JOIN oauth2_sessions os\n ON os.id = oc.oauth2_session_id\n WHERE oc.code = $1\n ",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
@@ -421,6 +409,11 @@
|
||||
"ordinal": 4,
|
||||
"name": "scope!",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 5,
|
||||
"name": "nonce",
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
@@ -433,10 +426,23 @@
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
false,
|
||||
true
|
||||
]
|
||||
}
|
||||
},
|
||||
"88ac8783bd5881c42eafd9cf87a16fe6031f3153fd6a8618e689694584aeb2de": {
|
||||
"query": "\n DELETE FROM oauth2_access_tokens\n WHERE id = $1\n ",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
}
|
||||
},
|
||||
"9ba45ab114b656105cc46b0c10fb05769860fcdc05eaf54d6225640fb914dab9": {
|
||||
"query": "\n INSERT INTO user_session_authentications (session_id)\n VALUES ($1)\n RETURNING created_at\n ",
|
||||
"describe": {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
use anyhow::Context;
|
||||
use chrono::Duration;
|
||||
use data_encoding::BASE64URL_NOPAD;
|
||||
use jwt_compact::{Claims, Header, TimeOptions};
|
||||
use oauth2_types::{
|
||||
errors::{InvalidGrant, OAuth2Error},
|
||||
@@ -23,6 +24,8 @@ use oauth2_types::{
|
||||
};
|
||||
use rand::thread_rng;
|
||||
use serde::Serialize;
|
||||
use serde_with::skip_serializing_none;
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::{pool::PoolConnection, Acquire, PgPool, Postgres};
|
||||
use url::Url;
|
||||
use warp::{Filter, Rejection, Reply};
|
||||
@@ -43,6 +46,7 @@ use crate::{
|
||||
tokens,
|
||||
};
|
||||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Serialize, Debug)]
|
||||
struct CustomClaims {
|
||||
#[serde(rename = "iss")]
|
||||
@@ -51,6 +55,9 @@ struct CustomClaims {
|
||||
subject: String,
|
||||
#[serde(rename = "aud")]
|
||||
audiences: Vec<String>,
|
||||
nonce: Option<String>,
|
||||
at_hash: String,
|
||||
c_hash: String,
|
||||
}
|
||||
|
||||
pub fn filter(
|
||||
@@ -93,6 +100,16 @@ async fn token(
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
fn hash<H: Digest>(mut hasher: H, token: &str) -> anyhow::Result<String> {
|
||||
hasher.update(token);
|
||||
let hash = hasher.finalize();
|
||||
// Left-most 128bit
|
||||
let bits = hash
|
||||
.get(..16)
|
||||
.context("failed to get first 128 bits of hash")?;
|
||||
Ok(BASE64URL_NOPAD.encode(bits))
|
||||
}
|
||||
|
||||
async fn authorization_code_grant(
|
||||
grant: &AuthorizationCodeGrant,
|
||||
client: &OAuth2ClientConfig,
|
||||
@@ -138,6 +155,9 @@ async fn authorization_code_grant(
|
||||
// TODO: get that from the session
|
||||
subject: "random-subject".to_string(),
|
||||
audiences: vec![client.client_id.clone()],
|
||||
nonce: code.nonce,
|
||||
at_hash: hash(Sha256::new(), &access_token.token).wrap_error()?,
|
||||
c_hash: hash(Sha256::new(), &grant.code).wrap_error()?,
|
||||
})
|
||||
.set_duration_and_issuance(&options, Duration::minutes(30));
|
||||
let id_token = keys
|
||||
|
||||
@@ -62,6 +62,7 @@ pub struct OAuth2CodeLookup {
|
||||
pub client_id: String,
|
||||
pub redirect_uri: String,
|
||||
pub scope: String,
|
||||
pub nonce: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn lookup_code(
|
||||
@@ -76,7 +77,8 @@ pub async fn lookup_code(
|
||||
os.id AS "oauth2_session_id!",
|
||||
os.client_id AS "client_id!",
|
||||
os.redirect_uri,
|
||||
os.scope AS "scope!"
|
||||
os.scope AS "scope!",
|
||||
os.nonce
|
||||
FROM oauth2_codes oc
|
||||
INNER JOIN oauth2_sessions os
|
||||
ON os.id = oc.oauth2_session_id
|
||||
|
||||
Reference in New Issue
Block a user