better_auth_api/plugins/oauth/
providers.rs1use serde_json::Value;
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Default)]
6pub struct OAuthConfig {
7 pub providers: HashMap<String, OAuthProvider>,
8}
9
10#[derive(Debug, Clone)]
12pub struct OAuthUserInfo {
13 pub id: String,
14 pub email: String,
15 pub name: Option<String>,
16 pub image: Option<String>,
17 pub email_verified: bool,
18}
19
20#[derive(Debug, Clone)]
22pub struct OAuthProvider {
23 pub client_id: String,
24 pub client_secret: String,
25 pub auth_url: String,
26 pub token_url: String,
27 pub user_info_url: String,
28 pub scopes: Vec<String>,
29 pub map_user_info: fn(Value) -> Result<OAuthUserInfo, String>,
30}
31
32impl OAuthProvider {
33 pub fn google(client_id: &str, client_secret: &str) -> Self {
34 Self {
35 client_id: client_id.to_string(),
36 client_secret: client_secret.to_string(),
37 auth_url: "https://accounts.google.com/o/oauth2/v2/auth".to_string(),
38 token_url: "https://oauth2.googleapis.com/token".to_string(),
39 user_info_url: "https://www.googleapis.com/oauth2/v3/userinfo".to_string(),
40 scopes: vec![
41 "openid".to_string(),
42 "email".to_string(),
43 "profile".to_string(),
44 ],
45 map_user_info: |v| {
46 Ok(OAuthUserInfo {
47 id: v["sub"].as_str().ok_or("missing sub")?.to_string(),
48 email: v["email"].as_str().ok_or("missing email")?.to_string(),
49 name: v["name"].as_str().map(String::from),
50 image: v["picture"].as_str().map(String::from),
51 email_verified: v["email_verified"].as_bool().unwrap_or(false),
52 })
53 },
54 }
55 }
56
57 pub fn github(client_id: &str, client_secret: &str) -> Self {
58 Self {
59 client_id: client_id.to_string(),
60 client_secret: client_secret.to_string(),
61 auth_url: "https://github.com/login/oauth/authorize".to_string(),
62 token_url: "https://github.com/login/oauth/access_token".to_string(),
63 user_info_url: "https://api.github.com/user".to_string(),
64 scopes: vec!["user:email".to_string()],
65 map_user_info: |v| {
66 Ok(OAuthUserInfo {
67 id: v["id"]
68 .as_i64()
69 .map(|i| i.to_string())
70 .or_else(|| v["id"].as_str().map(String::from))
71 .ok_or("missing id")?,
72 email: v["email"].as_str().ok_or("missing email")?.to_string(),
73 name: v["name"].as_str().map(String::from),
74 image: v["avatar_url"].as_str().map(String::from),
75 email_verified: true,
76 })
77 },
78 }
79 }
80
81 pub fn discord(client_id: &str, client_secret: &str) -> Self {
82 Self {
83 client_id: client_id.to_string(),
84 client_secret: client_secret.to_string(),
85 auth_url: "https://discord.com/api/oauth2/authorize".to_string(),
86 token_url: "https://discord.com/api/oauth2/token".to_string(),
87 user_info_url: "https://discord.com/api/users/@me".to_string(),
88 scopes: vec!["identify".to_string(), "email".to_string()],
89 map_user_info: |v| {
90 Ok(OAuthUserInfo {
91 id: v["id"].as_str().ok_or("missing id")?.to_string(),
92 email: v["email"].as_str().ok_or("missing email")?.to_string(),
93 name: v["username"].as_str().map(String::from),
94 image: v["avatar"].as_str().map(|a| {
95 format!(
96 "https://cdn.discordapp.com/avatars/{}/{}.png",
97 v["id"].as_str().unwrap_or(""),
98 a
99 )
100 }),
101 email_verified: v["verified"].as_bool().unwrap_or(false),
102 })
103 },
104 }
105 }
106}