userp/oauth/provider/
spotify.rs1use super::custom::OAuthCustomProvider;
2use crate::oauth::OAuthProviderUser;
3use anyhow::Context;
4use serde_json::Value;
5
6pub struct SpotifyOAuthProvider;
7
8impl SpotifyOAuthProvider {
9 #[allow(clippy::new_ret_no_self)]
10 pub fn new(
11 client_id: impl Into<String>,
12 client_secret: impl Into<String>,
13 ) -> OAuthCustomProvider {
14 OAuthCustomProvider::new_with_callback(
15 "spotify",
16 "Spotify",
17 client_id,
18 client_secret,
19 "https://accounts.spotify.com/authorize",
20 "https://accounts.spotify.com/api/token",
21 &["user-read-email"],
22 |access_token| async move {
23 let client = reqwest::Client::new();
24
25 let res = client
26 .get("https://api.spotify.com/v1/me")
27 .header("Accept", "application/json")
28 .bearer_auth(access_token)
29 .send()
30 .await?
31 .json::<Value>()
32 .await?;
33
34 let id = res
35 .as_object()
36 .and_then(|obj| obj.get("id").and_then(|id| id.as_str()))
37 .context("Missing id")?
38 .to_string();
39
40 let email = res
41 .as_object()
42 .and_then(|obj| obj.get("email").and_then(|id| id.as_str()))
43 .map(|name| name.to_string());
44
45 let name = res
46 .as_object()
47 .and_then(|obj| obj.get("display_name").and_then(|id| id.as_str()))
48 .map(|name| name.to_string());
49
50 Ok(OAuthProviderUser {
51 id,
52 email,
53 name,
54 email_verified: false,
55 })
56 },
57 )
58 .expect("Built in providers should work")
59 }
60}