//! Response of your app's sign in with apple
use serde::{Deserialize, Serialize};
use crate::{
auth_token::{AuthTokenRequest, AuthTokenResponse},
common::error::Result,
object::AppleClientSecretPayload,
};
use super::identity_token::AppleIdentityToken;
/// Application side sign in with apple result
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SIWAResponse {
identity_token: String,
authorization_code: String,
client_id: String,
}
impl SIWAResponse {
pub fn new(
identity_token: String,
authorization_code: String,
client_id: String,
) -> Self {
Self {
identity_token,
authorization_code,
client_id,
}
}
/// > <https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/verifying_a_user>
///
/// You can use this method whenever you want to verify user's identity token.
/// `iss` and `aud` should be always valid.
/// Validation of `exp` can be turned off.
///
/// Leak of decoded identity token will not harm your service,
/// but it may include user's sensitive information (e.g. real email address).
pub async fn verify_identity_token(
&self,
validate_exp: bool,
) -> Result<AppleIdentityToken> {
let id_token = AppleIdentityToken::decode(
&self.identity_token,
&self.client_id,
validate_exp,
)
.await?;
Ok(id_token)
}
/// > <https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens>
///
/// - `key_id`: `kid` field. issued by apple.
/// - `pkey`: your private key. issued by apple.
/// - `team_id`: `iss` field. your developers account team id.
/// - `client_id`: `client_id` field. Your app/service/bundle id.
/// - `issued_at`: `iat` field. Seconds since unix epoch.
/// - `valid_while`: <Duration> to calcaulte `exp`.
///
/// When user request authroization through Sign In with Apple
/// from your services (web, app or something), apple issues
/// authorization code.
///
/// You need to validate authorization code via apple's server.
/// And then, you will get refresh token and access token with id token.
/// Validating authorization code should be performed within ***five minutes***.
///
///
pub async fn validate_auth_code(
&self,
client_secret: AppleClientSecretPayload,
redirect_uri: Option<String>,
) -> Result<AuthTokenResponse> {
let request = AuthTokenRequest::new(
self.client_id.to_owned(),
client_secret,
redirect_uri,
)?;
request.validate_auth_code(&self.authorization_code).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn validate_test() -> Result<()> {
let token = "eyJraWQiOiJZdXlYb1kiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoidG93bi5waWVjZS5hcHAiLCJleHAiOjE2NTM3MjU1MjYsImlhdCI6MTY1MzYzOTEyNiwic3ViIjoiMDAwNDIyLjJkMWNlODE2Njk2ZTRkYTBiMjhhOTk3ZmJkYTBiYzU5LjA5MzEiLCJhdF9oYXNoIjoidVFGWTBVMmdjTkhBRzlacjluZ0hGdyIsImVtYWlsIjoidXN3dXJpa2lqaUBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJhdXRoX3RpbWUiOjE2NTM2MzkxMDEsIm5vbmNlX3N1cHBvcnRlZCI6dHJ1ZX0.i3Dp01s6RGc5NBu97Vw-VdvNi6ejilME1m1e-27Lv2P7nKUPUos2HJb888oiQRroC7E3zihDAL53FbsFp7kgGDVTt9R68YKdaM-Nwl97ywUP9ehVk1KuUd9rd4cHEN8Cms7YnJErSMIOmj3mMjg6ISEGQHrOPVtG9fk_9HqK7mcyxtnsAM9K-CxGbwzgVqJBgQK45qBq-lNPYnOJOKO6DQfOA86X0csYZ2wqFlc89Z3APOkL_Q_Y69ERq1YHyRg4IfW9puTURhjWRNpW_7Qt4RhP4ewWRKsJ1fr_E64bbpnLFyepJLBHYePNiEbfZfd0k_crdSS4_fuzHWHFsDqddg";
let siwa = SIWAResponse::new(
token.to_owned(),
"".to_owned(),
"town.piece.app".to_owned(),
);
let result = siwa.verify_identity_token(false).await?;
println!("{result:?}");
Ok(())
}
}