cesium_oauth/auth/
token.rs

1use log::{debug, trace};
2use serde::{Deserialize, Serialize};
3
4use crate::apps::{AppInfo, RegisteredApp};
5use crate::error::Error;
6
7/// Response for obtaining an access token. Really the only field you need
8/// is the `access_token`, but the other fields are provided for convenience.
9#[derive(Serialize, Deserialize, Clone, Debug)]
10pub struct TokenResponse {
11    /// An OAuth token to be used for authorization
12    pub access_token: String,
13    /// The OAuth token type. Mastodon uses `Bearer` tokens
14    pub token_type: String,
15    /// The OAuth scopes granted by this token, space-separated
16    pub scope: String,
17    /// When the token was generated (UNIX Timestamp)
18    pub created_at: u32,
19}
20
21/// Obtain an access token from an instance using an authorization code.
22/// The `registered_app` and `app` need to be matching.
23///
24/// # Errors
25///
26/// This method fails if there was an issue connecting to the instance,
27/// or if the authorization failed.
28///
29/// # Example
30///
31/// ```no_run
32/// # use cesium_oauth::auth;
33/// # async fn run() -> Result<(), cesium_oauth::Error> {
34/// let account = auth::verify_credentials("mastodon.art", "[TOKEN]").await?;
35/// # Ok(())
36/// # }
37pub async fn get_auth_token(
38    instance_domain: &str,
39    authorization_code: &str,
40    registered_app: &RegisteredApp,
41    app: &AppInfo,
42) -> Result<TokenResponse, Error> {
43    let url = format!("https://{instance_domain}/oauth/token");
44    let params = [
45        ("grant_type", "authorization_code"),
46        ("code", authorization_code),
47        ("client_id", &registered_app.client_id),
48        ("client_secret", &registered_app.client_secret),
49        ("redirect_uri", &app.redirect_uri),
50        ("scope", "read:accounts"),
51    ];
52
53    debug!("Obtaining access token from `{instance_domain}`");
54    trace!("Token obtain url is {url}");
55
56    let client = reqwest::Client::new();
57    let response = client
58        .post(&url)
59        .form(&params)
60        .send()
61        .await
62        .map_err(|_| Error::TokenObtainError("Failed to connect to server"))?;
63
64    trace!("Response status {}", response.status());
65    if response.status() != 200 {
66        return Err(Error::TokenObtainError("Authorization failure"));
67    }
68
69    let registered_app: TokenResponse = response
70        .json()
71        .await
72        .map_err(|_| Error::TokenObtainError("Unable to parse response from server"))?;
73
74    debug!("Access token obtained");
75
76    Ok(registered_app)
77}