Skip to main content

casdoor_rust_sdk/
token.rs

1// Copyright 2026 The Casdoor Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::HashMap;
16
17use serde::{Deserialize, Serialize};
18
19use crate::client::{get_admin_id, get_owner, Client, PostBody, Response};
20use crate::error::Result;
21use crate::serde_util::null_to_default;
22
23/// Token has the same definition as
24/// <https://github.com/casdoor/casdoor/blob/master/object/token.go>.
25#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "camelCase", default)]
27pub struct Token {
28    pub owner: String,
29    pub name: String,
30    pub created_time: String,
31
32    pub application: String,
33    pub organization: String,
34    pub user: String,
35
36    pub code: String,
37    pub access_token: String,
38    pub refresh_token: String,
39    pub access_token_hash: String,
40    pub refresh_token_hash: String,
41    pub expires_in: i32,
42    pub scope: String,
43    pub token_type: String,
44    pub grant_type: String,
45    pub code_challenge: String,
46    pub code_is_used: bool,
47    pub code_expire_in: i64,
48    /// RFC 8707 Resource Indicator.
49    pub resource: String,
50    /// RFC 9449 DPoP JWK thumbprint binding.
51    #[serde(rename = "dPoPJkt")]
52    pub dpop_jkt: String,
53    pub session_id: String,
54}
55
56/// The result of the RFC 7662 token introspection.
57#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(default)]
59pub struct IntrospectTokenResult {
60    pub active: bool,
61    pub client_id: String,
62    pub username: String,
63    pub token_type: String,
64    pub exp: u64,
65    pub iat: u64,
66    pub nbf: u64,
67    pub sub: String,
68    #[serde(deserialize_with = "null_to_default")]
69    pub aud: Vec<String>,
70    pub iss: String,
71    pub jti: String,
72}
73
74impl Client {
75    /// Get all the tokens.
76    pub async fn get_tokens(&self) -> Result<Vec<Token>> {
77        self.get_list("get-tokens", &[("owner", "admin")]).await
78    }
79
80    /// Get one page of the tokens, together with the total number of the tokens.
81    pub async fn get_pagination_tokens(
82        &self,
83        p: i32,
84        page_size: i32,
85        query: &[(&str, &str)],
86    ) -> Result<(Vec<Token>, i32)> {
87        self.get_pagination("get-tokens", "admin", p, page_size, query)
88            .await
89    }
90
91    /// Get one token by name.
92    pub async fn get_token(&self, name: &str) -> Result<Option<Token>> {
93        self.get_object("get-token", &[("id", &get_admin_id(name))])
94            .await
95    }
96
97    /// Add a token.
98    pub async fn add_token(&self, token: &Token) -> Result<bool> {
99        Ok(self.modify_token("add-token", token, &[]).await?.1)
100    }
101
102    /// Update a token.
103    pub async fn update_token(&self, token: &Token) -> Result<bool> {
104        Ok(self.modify_token("update-token", token, &[]).await?.1)
105    }
106
107    /// Update only the given columns of a token.
108    pub async fn update_token_for_columns(&self, token: &Token, columns: &[&str]) -> Result<bool> {
109        Ok(self.modify_token("update-token", token, columns).await?.1)
110    }
111
112    /// Delete a token.
113    pub async fn delete_token(&self, token: &Token) -> Result<bool> {
114        Ok(self.modify_token("delete-token", token, &[]).await?.1)
115    }
116
117    /// Introspect an access token or a refresh token, `token_type_hint` is
118    /// `"access_token"` or `"refresh_token"`.
119    pub async fn introspect_token(
120        &self,
121        token: &str,
122        token_type_hint: &str,
123    ) -> Result<IntrospectTokenResult> {
124        let fields = HashMap::from([
125            ("token".to_string(), token.to_string()),
126            ("token_type_hint".to_string(), token_type_hint.to_string()),
127        ]);
128
129        let url = self.get_url("login/oauth/introspect", &[]);
130        let bytes = self.do_post_bytes_raw(&url, PostBody::Form(fields)).await?;
131
132        Ok(serde_json::from_slice(&bytes)?)
133    }
134
135    async fn modify_token(
136        &self,
137        action: &str,
138        token: &Token,
139        columns: &[&str],
140    ) -> Result<(Response, bool)> {
141        let mut token = token.clone();
142        token.owner = get_owner(&token.owner, "admin");
143
144        let id = format!("{}/{}", token.owner, token.name);
145        self.modify_object(action, &id, &token, columns).await
146    }
147}