pub mod types;
use std::sync::Arc;
use crate::{error::Error, http::HttpClient};
use types::{
AccessToken, AuthorizationDecision, AuthorizeParams, CreateIdentityParams, CreateTokenParams,
Identity, JsonPatchOp, OidcProvider, SecuritySettings, TokenInfo,
};
pub struct Gate {
http: Arc<HttpClient>,
api_key: String,
}
impl std::fmt::Debug for Gate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Gate").finish_non_exhaustive()
}
}
impl Gate {
pub fn new(api_key: impl Into<String>) -> Self {
let key = api_key.into();
Self::builder()
.api_key(&key)
.build()
.expect("failed to build Gate client")
}
pub fn builder() -> GateBuilder {
GateBuilder::default()
}
pub(crate) fn from_http(http: Arc<HttpClient>, api_key: String) -> Self {
Self { http, api_key }
}
pub fn identities(&self) -> IdentitiesClient {
IdentitiesClient {
http: Arc::clone(&self.http),
}
}
pub fn tokens(&self) -> TokensClient {
TokensClient {
http: Arc::clone(&self.http),
api_key: self.api_key.clone(),
}
}
pub fn settings(&self) -> SettingsClient {
SettingsClient {
http: Arc::clone(&self.http),
}
}
pub async fn authorize(&self, params: AuthorizeParams) -> Result<AuthorizationDecision, Error> {
self.http.post("/v1/gate/authorize", ¶ms, false).await
}
pub async fn get_enabled_providers(&self, tenant_id: &str) -> Result<Vec<String>, Error> {
#[derive(serde::Deserialize)]
struct Wrapper {
providers: Vec<String>,
}
let wrapped: Wrapper = self
.http
.get(&format!("/public/gate/providers/{tenant_id}"))
.await?;
Ok(wrapped.providers)
}
pub async fn create_login_flow(&self) -> Result<serde_json::Value, Error> {
self.http.get("/v1/gate/auth/login").await
}
}
#[derive(Default)]
pub struct GateBuilder {
api_key: Option<String>,
base_url: Option<String>,
timeout_secs: Option<u64>,
}
impl GateBuilder {
pub fn api_key(mut self, key: impl Into<String>) -> Self {
self.api_key = Some(key.into());
self
}
pub fn base_url(mut self, url: impl Into<String>) -> Self {
self.base_url = Some(url.into());
self
}
pub fn timeout_secs(mut self, secs: u64) -> Self {
self.timeout_secs = Some(secs);
self
}
pub fn build(self) -> Result<Gate, Error> {
let key = self
.api_key
.ok_or_else(|| Error::Config("gate API key is required".into()))?;
let http = HttpClient::new(&key, self.base_url, self.timeout_secs)?;
Ok(Gate {
http: Arc::new(http),
api_key: key,
})
}
}
pub struct IdentitiesClient {
http: Arc<HttpClient>,
}
impl IdentitiesClient {
pub async fn create(&self, params: CreateIdentityParams) -> Result<Identity, Error> {
self.http.post("/v1/gate/identities", ¶ms, false).await
}
pub async fn get(&self, identity_id: &str) -> Result<Identity, Error> {
self.http
.get(&format!("/v1/gate/identities/{identity_id}"))
.await
}
pub async fn patch(&self, identity_id: &str, ops: Vec<JsonPatchOp>) -> Result<Identity, Error> {
self.http
.patch(&format!("/v1/gate/identities/{identity_id}"), &ops)
.await
}
pub async fn delete(&self, identity_id: &str) -> Result<(), Error> {
self.http
.delete(&format!("/v1/gate/identities/{identity_id}"))
.await
}
pub async fn set_state(&self, identity_id: &str, state: &str) -> Result<Identity, Error> {
#[derive(serde::Serialize)]
struct StateBody<'a> {
state: &'a str,
}
self.http
.patch(
&format!("/v1/gate/identities/{identity_id}/state"),
&StateBody { state },
)
.await
}
pub async fn activate(&self, identity_id: &str) -> Result<Identity, Error> {
self.set_state(identity_id, "active").await
}
pub async fn deactivate(&self, identity_id: &str) -> Result<Identity, Error> {
self.set_state(identity_id, "inactive").await
}
pub async fn resend_verification(&self, identity_id: &str) -> Result<(), Error> {
self.http
.post_discard(&format!("/v1/gate/identities/{identity_id}/resend-verification"))
.await
}
}
pub struct TokensClient {
http: Arc<HttpClient>,
api_key: String,
}
impl TokensClient {
pub async fn create(&self, params: CreateTokenParams) -> Result<AccessToken, Error> {
#[derive(serde::Serialize)]
struct CreateTokenBody {
api_key: String,
subject: String,
#[serde(skip_serializing_if = "Option::is_none")]
scopes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
ttl_seconds: Option<u64>,
}
let body = CreateTokenBody {
api_key: self.api_key.clone(),
subject: params.subject,
scopes: params.scopes,
ttl_seconds: params.ttl_seconds,
};
self.http.post("/v1/gate/tokens", &body, true).await
}
pub async fn introspect(&self, access_token: &str) -> Result<TokenInfo, Error> {
#[derive(serde::Serialize)]
struct IntrospectBody<'a> {
access_token: &'a str,
}
self.http
.post(
"/v1/gate/tokens/introspect",
&IntrospectBody { access_token },
false,
)
.await
}
}
pub struct SettingsClient {
http: Arc<HttpClient>,
}
impl SettingsClient {
pub async fn get_security(&self) -> Result<SecuritySettings, Error> {
self.http.get("/v1/gate/settings/security").await
}
pub async fn update_security(&self, settings: SecuritySettings) -> Result<(), Error> {
self.http
.put_discard("/v1/gate/settings/security", &settings)
.await
}
pub async fn get_oidc_providers(&self) -> Result<Vec<OidcProvider>, Error> {
#[derive(serde::Deserialize)]
struct Wrapper {
providers: Vec<OidcProvider>,
}
let wrapped: Wrapper = self.http.get("/v1/gate/settings/oidc-providers").await?;
Ok(wrapped.providers)
}
pub async fn update_oidc_providers(
&self,
providers: Vec<OidcProvider>,
) -> Result<Vec<OidcProvider>, Error> {
#[derive(serde::Serialize)]
struct Body {
providers: Vec<OidcProvider>,
}
#[derive(serde::Deserialize)]
struct Wrapper {
providers: Vec<OidcProvider>,
}
let wrapped: Wrapper = self
.http
.put("/v1/gate/settings/oidc-providers", &Body { providers })
.await?;
Ok(wrapped.providers)
}
}