Emit id_token

This commit is contained in:
2022-04-13 19:07:58 +02:00
parent d69b44528e
commit 53a540139c
10 changed files with 734 additions and 25 deletions

View File

@ -1,16 +1,20 @@
use actix::Addr;
use actix_identity::Identity;
use actix_web::{HttpRequest, HttpResponse, Responder, web};
use actix_web::error::ErrorUnauthorized;
use askama::Template;
use crate::actors::openid_sessions_actor;
use crate::actors::openid_sessions_actor::{OpenIDSessionsActor, Session, SessionID};
use crate::constants::{AUTHORIZE_URI, OPEN_ID_ACCESS_TOKEN_LEN, OPEN_ID_ACCESS_TOKEN_TIMEOUT, OPEN_ID_AUTHORIZATION_CODE_LEN, OPEN_ID_AUTHORIZATION_CODE_TIMEOUT, OPEN_ID_REFRESH_TOKEN_LEN, OPEN_ID_REFRESH_TOKEN_TIMEOUT, OPEN_ID_SESSION_LEN, TOKEN_URI};
use crate::constants::{AUTHORIZE_URI, CERT_URI, OPEN_ID_ACCESS_TOKEN_LEN, OPEN_ID_ACCESS_TOKEN_TIMEOUT, OPEN_ID_AUTHORIZATION_CODE_LEN, OPEN_ID_AUTHORIZATION_CODE_TIMEOUT, OPEN_ID_REFRESH_TOKEN_LEN, OPEN_ID_REFRESH_TOKEN_TIMEOUT, OPEN_ID_SESSION_LEN, TOKEN_URI};
use crate::controllers::base_controller::FatalErrorPage;
use crate::data::app_config::AppConfig;
use crate::data::client::{ClientID, ClientManager};
use crate::data::current_user::CurrentUser;
use crate::data::id_token::IdToken;
use crate::data::jwt_signer::{JsonWebKey, JWTSigner};
use crate::data::openid_config::OpenIDConfig;
use crate::data::session_identity::SessionIdentity;
use crate::utils::string_utils::rand_str;
use crate::utils::time::time;
@ -20,7 +24,7 @@ pub async fn get_configuration(app_conf: web::Data<AppConfig>) -> impl Responder
authorization_endpoint: app_conf.full_url(AUTHORIZE_URI),
token_endpoint: app_conf.full_url(TOKEN_URI),
userinfo_endpoint: app_conf.full_url("openid/userinfo"),
jwks_uri: app_conf.full_url("openid/jwks_uri"),
jwks_uri: app_conf.full_url(CERT_URI),
scopes_supported: vec!["openid", "profile", "email"],
response_types_supported: vec!["code", "id_token", "token id_token"],
subject_types_supported: vec!["public"],
@ -41,7 +45,7 @@ pub struct AuthorizeQuery {
/// REQUIRED. OAuth 2.0 Client Identifier valid at the Authorization Server.
client_id: ClientID,
/// REQUIRED. Redirection URI to which the response will be sent. This URI MUST exactly match one of the Redirection URI values for the Client pre-registered at the OpenID Provider, with the matching performed as described in Section 6.2.1 of [RFC3986] (Simple String Comparison). When using this flow, the Redirection URI SHOULD use the https scheme; however, it MAY use the http scheme, provided that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0, and provided the OP allows the use of http Redirection URIs in this case. The Redirection URI MAY use an alternate scheme, such as one that is intended to identify a callback into a native application.
/// REQUIRED. Redirection URI to which the response will be sent. This URI MUST exactly match one of the Redirection URI values for the Client pre-registered at the OpenID Provider, with the matching performed as described in Section 6.2.1 of RFC3986 (Simple String Comparison). When using this flow, the Redirection URI SHOULD use the https scheme; however, it MAY use the http scheme, provided that the Client Type is confidential, as defined in Section 2.1 of OAuth 2.0, and provided the OP allows the use of http Redirection URIs in this case. The Redirection URI MAY use an alternate scheme, such as one that is intended to identify a callback into a native application.
redirect_uri: String,
/// RECOMMENDED. Opaque value used to maintain state between the request and the callback. Typically, Cross-Site Request Forgery (CSRF, XSRF) mitigation is done by cryptographically binding the value of this parameter with a browser cookie.
@ -50,7 +54,7 @@ pub struct AuthorizeQuery {
/// OPTIONAL. String value used to associate a Client session with an ID Token, and to mitigate replay attacks. The value is passed through unmodified from the Authentication Request to the ID Token. Sufficient entropy MUST be present in the nonce values used to prevent attackers from guessing values.
nonce: Option<String>,
/// OPTIONAL - https://ldapwiki.com/wiki/Code_challenge_method
/// OPTIONAL - <https://ldapwiki.com/wiki/Code_challenge_method>
code_challenge: Option<String>,
code_challenge_method: Option<String>,
}
@ -70,7 +74,7 @@ fn error_redirect(query: &AuthorizeQuery, error: &str, description: &str) -> Htt
.finish()
}
pub async fn authorize(user: CurrentUser, query: web::Query<AuthorizeQuery>,
pub async fn authorize(user: CurrentUser, id: Identity, query: web::Query<AuthorizeQuery>,
clients: web::Data<ClientManager>,
sessions: web::Data<Addr<OpenIDSessionsActor>>) -> impl Responder {
let client = match clients.find_by_id(&query.client_id) {
@ -123,6 +127,7 @@ pub async fn authorize(user: CurrentUser, query: web::Query<AuthorizeQuery>,
session_id: SessionID(rand_str(OPEN_ID_SESSION_LEN)),
client: client.id,
user: user.uid.clone(),
auth_time: SessionIdentity(&id).auth_time(),
redirect_uri,
authorization_code: rand_str(OPEN_ID_AUTHORIZATION_CODE_LEN),
authorization_code_expire_at: time() + OPEN_ID_AUTHORIZATION_CODE_TIMEOUT,
@ -168,7 +173,11 @@ pub struct TokenResponse {
pub async fn token(req: HttpRequest,
query: web::Form<TokenQuery>,
clients: web::Data<ClientManager>,
sessions: web::Data<Addr<OpenIDSessionsActor>>) -> actix_web::Result<HttpResponse> {
app_config: web::Data<AppConfig>,
sessions: web::Data<Addr<OpenIDSessionsActor>>,
jwt_signer: web::Data<JWTSigner>) -> actix_web::Result<HttpResponse> {
// TODO : add refresh tokens : https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens
// TODO : check auth challenge
// Extraction authentication information
let authorization_header = req.headers().get("authorization");
@ -258,6 +267,17 @@ pub async fn token(req: HttpRequest,
.await.unwrap();
// Generate id token
let token = IdToken {
issuer: app_config.website_origin.to_string(),
subject_identifier: session.user,
audience: session.client.0.to_string(),
expiration_time: session.access_token_expire_at,
issued_at: time(),
auth_time: session.auth_time,
nonce: session.nonce,
};
Ok(HttpResponse::Ok()
.append_header(("Cache-Control", "no-store"))
.append_header(("Pragam", "no-cache"))
@ -266,6 +286,15 @@ pub async fn token(req: HttpRequest,
token_type: "Bearer",
refresh_token: session.refresh_token,
expires_in: session.access_token_expire_at - time(),
id_token: session.session_id.0,
id_token: jwt_signer.sign_token(token.to_jwt_claims())?
}))
}
#[derive(serde::Serialize)]
struct CertsResponse {
keys: Vec<JsonWebKey>,
}
pub async fn cert_uri(jwt_signer: web::Data<JWTSigner>) -> impl Responder {
HttpResponse::Ok().json(CertsResponse { keys: vec![jwt_signer.get_json_web_key()] })
}