1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
//! Deezer API authentication.
use std::error::Error;
use std::fmt::Display;
use std::time::Duration;
use actix_web::web::Data;
use actix_web::{App, HttpServer};
use serde::Deserialize;
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use tokio::{select, spawn};
use crate::auth::service::retrieve_auth_code;
use crate::errors::auth::AuthPromptHandlerError;
mod service;
const AUTH_URL: &str = "https://connect.deezer.com/oauth/auth.php";
/// A Deezer authentication code.
#[derive(Debug, Deserialize, Clone)]
pub struct AuthCode(String);
impl Display for AuthCode
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
{
self.0.fmt(f)
}
}
/// A Deezer authentication prompt handler.
pub struct AuthPromptHandler
{
/// URL to the Deezer authentication prompt.
pub auth_prompt_url: String,
/// Handle for the running authentication prompt handler.
///
/// Finishes when a single authentication has occurred.
pub handler: JoinHandle<Result<AuthCode, Box<dyn Error + Send + Sync>>>,
}
impl AuthPromptHandler
{
/// Runs a web server that handles a Deezer authentication prompt.
///
/// The argument `address` must be in the same domain as the domain you defined when
/// you created the application with the ID `app_id`.
///
/// # Errors
/// Will return Err if the web server created is unable to bind to `address`.
pub async fn run(
app_id: u32,
address: String,
port: u16,
uri_scheme: String,
) -> Result<Self, AuthPromptHandlerError>
{
let (done_tx, mut done_rx) = mpsc::channel::<AuthCode>(1);
let done_tx_data = Data::new(done_tx);
let server = HttpServer::new(move || {
App::new()
.app_data(done_tx_data.clone())
.service(retrieve_auth_code)
})
.bind((address.clone(), port))
.map_err(|_| AuthPromptHandlerError::BindAddressFailed)?;
let server_future = server.run();
let handle = spawn(async move {
let opt_auth_code = select! {
result = server_future => {
result.map(|_| None)
},
auth_code = async {
done_rx.recv().await
} => Ok(auth_code)
}?;
Ok(opt_auth_code.map_or_else(|| Err("No auth code was received"), Ok)?)
});
Ok(Self {
auth_prompt_url: format!(
"{}?app_id={}&redirect_uri={}://{}:{}&perms=basic_access",
AUTH_URL, app_id, uri_scheme, address, port
),
handler: handle,
})
}
}
/// A Deezer access token.
#[derive(Debug, Clone)]
pub struct AccessToken
{
/// The access token.
pub access_token: String,
/// The duration until the access token expires.
pub expires: Duration,
}
|