OpenID Connectって何?

発行者の署名付き名刺の概念を「IDトークン」と呼ぶ
IDトークンの発行者のことを「OpenIDプロバイダー」と呼ぶ
=> クライアント側の承認方式が公開鍵認証に似ている

登場人物
– ブラウザ
– クライアント: サービス提供者
– IdP: ブラウザは●●とクライアントに教える

#[derive(Debug)]
pub struct Client<P = Discovered, C: CompactJson + Claims = StandardClaims> {
    pub provider: P,
    pub client_id: String,
    pub client_secret: String,
    pub redirect_uri: Option<String>,
    pub http_client: reqwest::Client,
    pub jwks: Option<JWKSet<Empty>>,
    marker: PhantomData<C>,
}


#[tokio::main]
async fn main() {

    let client_id = std::env::var("CLIEnT_ID").expect("Unspecified CLIENT_ID as env var");
    let client_secret = std::env::var("CLIENT_SECRET").expect("Unspecified CLIENT_SECRET as env var");

    let issuer_url = std::env::var("ISSURE").unwrap_or("https://accounts.google.com".to_string());
    let issuer = reqwest::Url::parse(&issuer_url).unwrap();

    let redirect = Some(host("/login/oauth2/code/oidc"));

    let client = Arc::new(
        DiscoveredClient::discover(client_id, client_secret, redirect, issuer)
            .await
            .unwrap(),
    )
}

pub async fn authorize(
    Extension(oidc_client): Extension<Arc<OpenIdClient>>,
) -> (StatusCode, HeaderMap) {
 let origin_url = std::env::var("ORIGIN").unwrap_or(host(""));
 let auth_url = oidc_client.auth_url(&Options {
    scope: Some("openid email profile".into()),
    state: Some(origin_url),
    ..Default::default()
 });
 let url = String::from(auth_url);

 let mut headers = HeaderMap::new();
 let val = if let Ok(val) = HeaderValue::from_str(&url){
    val
 } else {
    return (StatusCode::INTERNAL_SERVER_ERROR, headers);
 };
 headers.insert(http::header::LOCATION, val);

 (StatusCode::FOUND, headers)
}

pub async fn login(
    Extension(oidc_client): Extension<Arc<OpenIDClient>>,
    login_query: Query<LoginQuery>,
) -> impl IntoResponse {
    let request_token = request_token(oidc_client, &login_query).await;
    match request_token {
        Ok(Some((token, user_info))) => {
            let login = user_info.preferred_username.clone();
            let email = user_info.email.clone();

            let user = Usr {
                id: usr_info.sub.clone().unwrap_or_default(),
                login,
                last_name: usr_info.family_name.clone(),
                first_name: user_info.name.clone(),
                email,
                activated: user_info.email_verified,
                image_url: user_info.picture.clone().map(|x| x.to_string()),
                lang_key: Some("en".to_string()),
                authorities: vec!["ROLE_USER".to_string()],
            };
            //...
        }
    }
}

async fn request_token(
    oidc_client: Arc<OpenIDClient>,
    login_query: &LoginQuery,
) -> anyhow::Result<Option<(Token, Userinfo)>> {
    let mut token: Token = oidc_client.request_token(&login_query.code).await?.into();

    if let Some(mut id_token) = token.id_token.as_mut() {
        oidc_client.decode_token(&mut id_token)?;
        oidc_client.validate_token(&id_token, None, None)?;
    } else {
        return Ok(None);
    }
    let userinfo = oidc_client.request_userinfo(&token).await?;

    Ok(Some((token, userinfo)))
}

OpenID Connectの全体像は大体わかったんだが、コレジャナイ感が強い。