deltachat/
token.rs

1//! # Token module.
2//!
3//! Functions to read/write token from/to the database. A token is any string associated with a key.
4//!
5//! Tokens are used in SecureJoin verification protocols.
6
7use anyhow::Result;
8use deltachat_derive::{FromSql, ToSql};
9
10use crate::context::Context;
11use crate::tools::{create_id, time};
12
13/// Token namespace
14#[derive(
15    Debug, Default, Display, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive, ToSql, FromSql,
16)]
17#[repr(u32)]
18pub enum Namespace {
19    #[default]
20    Unknown = 0,
21    Auth = 110,
22    InviteNumber = 100,
23}
24
25/// Saves a token to the database.
26pub async fn save(
27    context: &Context,
28    namespace: Namespace,
29    foreign_key: Option<&str>,
30    token: &str,
31    timestamp: i64,
32) -> Result<()> {
33    context
34        .sql
35        .execute(
36            "INSERT INTO tokens (namespc, foreign_key, token, timestamp) VALUES (?, ?, ?, ?)",
37            (namespace, foreign_key.unwrap_or(""), token, timestamp),
38        )
39        .await?;
40    Ok(())
41}
42
43/// Looks up most recently created token for a namespace / foreign key combination.
44///
45/// As there may be more than one such valid token,
46/// (eg. when a qr code token is withdrawn, recreated and revived later),
47/// use lookup() for qr-code creation only;
48/// do not use lookup() to check for token validity.
49///
50/// To check if a given token is valid, use exists().
51pub async fn lookup(
52    context: &Context,
53    namespace: Namespace,
54    foreign_key: Option<&str>,
55) -> Result<Option<String>> {
56    context
57        .sql
58        .query_get_value(
59            "SELECT token FROM tokens WHERE namespc=? AND foreign_key=? ORDER BY timestamp DESC LIMIT 1",
60            (namespace, foreign_key.unwrap_or("")),
61        )
62        .await
63}
64
65pub async fn lookup_or_new(
66    context: &Context,
67    namespace: Namespace,
68    foreign_key: Option<&str>,
69) -> Result<String> {
70    if let Some(token) = lookup(context, namespace, foreign_key).await? {
71        return Ok(token);
72    }
73
74    let token = create_id();
75    let timestamp = time();
76    save(context, namespace, foreign_key, &token, timestamp).await?;
77    Ok(token)
78}
79
80pub async fn exists(context: &Context, namespace: Namespace, token: &str) -> Result<bool> {
81    let exists = context
82        .sql
83        .exists(
84            "SELECT COUNT(*) FROM tokens WHERE namespc=? AND token=?;",
85            (namespace, token),
86        )
87        .await?;
88    Ok(exists)
89}
90
91/// Resets all tokens corresponding to the `foreign_key`.
92///
93/// `foreign_key` is a group ID to reset all group tokens
94/// or empty string to reset all setup contact tokens.
95pub async fn delete(context: &Context, foreign_key: &str) -> Result<()> {
96    context
97        .sql
98        .execute("DELETE FROM tokens WHERE foreign_key=?", (foreign_key,))
99        .await?;
100    Ok(())
101}