mirror of
https://git.nolog.cz/NoLog.cz/anon.git
synced 2025-01-31 05:03:35 +01:00
add userinfo endpoint
This commit is contained in:
parent
278fc65b2a
commit
e0b571a74a
1 changed files with 71 additions and 11 deletions
82
src/main.rs
82
src/main.rs
|
@ -404,23 +404,27 @@ async fn authenticate_endpoint(mut req: Request<AppState>) -> tide::Result {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The token is random because there are no resources protected by the token anyway.
|
let id_token = create_id_token(
|
||||||
let mut access_token = [0u8; 32];
|
req.state(),
|
||||||
SystemRandom::new().fill(&mut access_token)?;
|
&credentials.0,
|
||||||
let access_token = base64_coder::URL_SAFE_NO_PAD.encode(&access_token);
|
&code_info.account,
|
||||||
|
code_info.nonce,
|
||||||
|
)?;
|
||||||
|
|
||||||
req.state()
|
req.state()
|
||||||
.successful_logins
|
.successful_logins
|
||||||
.fetch_add(1, Ordering::Relaxed);
|
.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
// give access code
|
// give access code
|
||||||
Ok(Response::builder(200).body(json!({
|
Ok(Response::builder(200)
|
||||||
"access_token": access_token,
|
.body(json!({
|
||||||
"token_type": "Bearer",
|
"access_token": id_token,
|
||||||
"expires_in": TOKEN_EXPIRATION,
|
"token_type": "Bearer",
|
||||||
"id_token": create_id_token(req.state(), &credentials.0, &code_info.account, code_info.nonce)?,
|
"expires_in": TOKEN_EXPIRATION,
|
||||||
"scope": "openid profile email"
|
"id_token": id_token,
|
||||||
})).into())
|
"scope": "openid profile email"
|
||||||
|
}))
|
||||||
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn jwks_endpoint(req: Request<AppState>) -> tide::Result {
|
async fn jwks_endpoint(req: Request<AppState>) -> tide::Result {
|
||||||
|
@ -445,6 +449,7 @@ async fn configuration_endpoint(req: Request<AppState>) -> tide::Result {
|
||||||
"issuer": uri,
|
"issuer": uri,
|
||||||
"authorization_endpoint": uri.join("/authorize")?,
|
"authorization_endpoint": uri.join("/authorize")?,
|
||||||
"token_endpoint": uri.join("/token")?,
|
"token_endpoint": uri.join("/token")?,
|
||||||
|
"userinfo_endpoint": uri.join("/userinfo")?,
|
||||||
"jwks_uri": uri.join("/jwks")?,
|
"jwks_uri": uri.join("/jwks")?,
|
||||||
"response_types_supported": ["code"],
|
"response_types_supported": ["code"],
|
||||||
"subject_types_supported": ["pairwise"],
|
"subject_types_supported": ["pairwise"],
|
||||||
|
@ -493,6 +498,60 @@ logins_success {}
|
||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We expect the ID token as our access token
|
||||||
|
async fn userinfo_endpoint(req: Request<AppState>) -> tide::Result {
|
||||||
|
// this is all wrapped, because if something fails, then its the
|
||||||
|
// client's fault anyways (if there aren't any bugs, oh well...)
|
||||||
|
let resp = || {
|
||||||
|
let jwt = req
|
||||||
|
.header("Authorization")
|
||||||
|
.and_then(|v| v.get(0).unwrap().as_str().strip_prefix("Bearer "))
|
||||||
|
.ok_or(anyhow!("no bearer token"))?; // extract token
|
||||||
|
|
||||||
|
let (message, signature) = jwt.rsplit_once(".").ok_or(anyhow!("bad token form"))?;
|
||||||
|
|
||||||
|
let pk: ring::rsa::PublicKeyComponents<Vec<u8>> = req.state().signing_key.public().into();
|
||||||
|
|
||||||
|
pk.verify(
|
||||||
|
&ring::signature::RSA_PKCS1_2048_8192_SHA256,
|
||||||
|
message.as_bytes(),
|
||||||
|
&base64_coder::URL_SAFE_NO_PAD.decode(signature)?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let (_header, claims) = message.rsplit_once(".").ok_or(anyhow!("bad token form"))?;
|
||||||
|
let decoded_claims = base64_coder::URL_SAFE_NO_PAD.decode(claims)?;
|
||||||
|
let decoded_claims: serde_json::Value = serde_json::from_slice(&decoded_claims)?;
|
||||||
|
|
||||||
|
let now = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("no time travelling allowed before 1970")
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
if decoded_claims["exp"]
|
||||||
|
.as_u64()
|
||||||
|
.ok_or(anyhow!("invalid exp"))?
|
||||||
|
< now
|
||||||
|
{
|
||||||
|
Err(anyhow!("token expired"))
|
||||||
|
} else {
|
||||||
|
Ok(Response::builder(200)
|
||||||
|
.content_type("application/json")
|
||||||
|
.body(decoded_claims)
|
||||||
|
.build())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(unused_variables)] // rustc complains when building in release mode
|
||||||
|
resp().or_else(|e| {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
log::debug!("userinfo error: {}", e);
|
||||||
|
|
||||||
|
Ok(Response::builder(401)
|
||||||
|
.header("WWW-Authenticate", "Bearer error=\"invalid_token\"")
|
||||||
|
.build())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AuthStore {
|
pub struct AuthStore {
|
||||||
pub auths: HashMap<String, Authorization>,
|
pub auths: HashMap<String, Authorization>,
|
||||||
pub expirations: LinkedList<(String, u64)>,
|
pub expirations: LinkedList<(String, u64)>,
|
||||||
|
@ -563,6 +622,7 @@ async fn main() -> Result<()> {
|
||||||
.get(configuration_endpoint);
|
.get(configuration_endpoint);
|
||||||
app.at("/new-account").get(create_account_endpoint);
|
app.at("/new-account").get(create_account_endpoint);
|
||||||
app.at("/metrics").get(metrics_endpoint);
|
app.at("/metrics").get(metrics_endpoint);
|
||||||
|
app.at("/userinfo").get(userinfo_endpoint);
|
||||||
|
|
||||||
auto_serve_dir!(app, "/static", "static");
|
auto_serve_dir!(app, "/static", "static");
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue