siwa-async 0.5.1

Sign In With Apple Validation in Async Rust
Documentation
//! 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(())
	}
}