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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
//! A collection of functions for use with the OAuth2 API.
//!
//! This includes functions for easily generating URLs to redirect users to for
//! authorization.
pub use serenity::model::prelude::permissions::Permissions;
use crate::constants::BASE_AUTHORIZE_URI;
use percent_encoding;
use super::Scope;
use std::fmt::Write;
/// Creates a URL for a simple bot authorization flow.
///
/// This is a special,
/// server-less and callback-less OAuth2 flow that makes it
/// easy for users to add bots to guilds.
///
/// # Examples
///
/// Create an authorization URL for a bot requiring the "Add Reactions" and
/// "Send Messages" permissions:
///
/// ```rust
///
/// # fn main() {
/// use serenity_oauth::utils::Permissions;
///
/// let client_id = 249608697955745802;
/// let required = Permissions::ADD_REACTIONS | Permissions::SEND_MESSAGES;
/// let url = serenity_oauth::utils::bot_authorization_url(client_id, required);
///
/// // Assert that the expected URL is correct.
/// let expected = "https://discordapp.com/api/oauth2/authorize?client_id=249608697955745802&scope=bot&permissions=2112";
/// assert_eq!(url, expected);
/// # }
/// ```
pub fn bot_authorization_url(client_id: u64, permissions: Permissions)
-> String {
format!(
"{}?client_id={}&scope=bot&permissions={}",
BASE_AUTHORIZE_URI,
client_id,
permissions.bits(),
)
}
/// Creates a URL for an authorization code grant.
///
/// This will create a URL to redirect the user to, requesting the given scopes
/// for your client ID.
///
/// The given `redirect_uri` will automatically be URL encoded.
///
/// A state _should_ be passed, as recommended by RFC 6749. It is a unique
/// identifier for the user's request. When Discord redirects the user to the
/// given redirect URI, it will append a `state` parameter to the query. It will
/// match the state that you have recorded for that user. If it does not, there
/// was likely a request interception.
///
/// As well as the callback URL having the same `state` appended in the query
/// parameters, this will also append a `code`.
///
/// # Examples
///
/// Produce an authorization code grant URL for your client, requiring the
/// [`Scope::Identify`] and [`Scope::GuildsJoin`] scopes, and an example of a
/// state:
///
/// **Note**: Please randomly generate this using a crate like `rand`.
///
/// ```rust
/// use serenity_oauth::Scope;
///
/// let client_id = 249608697955745802;
/// let scopes = [Scope::GuildsJoin, Scope::Identify];
/// let state = "15773059ghq9183habn";
/// let redirect_uri = "https://myapplication.website";
///
/// let url = serenity_oauth::utils::authorization_code_grant_url(
/// client_id,
/// &scopes,
/// Some(state),
/// redirect_uri,
/// );
///
/// // Assert that the URL is correct.
/// let expected = "https://discordapp.com/api/oauth2/authorize?response_type=code&client_id=249608697955745802&redirect_uri=https%3A%2F%2Fmyapplication.website&scope=guilds.join%20identify&state=15773059ghq9183habn";
/// assert_eq!(url, expected);
/// ```
///
/// [`Scope::GuildsJoin`]: enum.Scope.html#variant.GuildsJoin
/// [`Scope::Identify`]: enum.Scope.html#variant.Identify
pub fn authorization_code_grant_url(
client_id: u64,
scopes: &[Scope],
state: Option<&str>,
redirect_uri: &str,
) -> String {
let mut base = String::from(BASE_AUTHORIZE_URI);
let uri = percent_encoding::percent_encode(
redirect_uri.as_bytes(),
percent_encoding::USERINFO_ENCODE_SET,
);
let _ = write!(
base,
"?response_type=code&client_id={}&redirect_uri={}&scope=",
client_id,
uri,
);
let scope_count = scopes.len();
for (i, scope) in scopes.iter().enumerate() {
let _ = write!(base, "{}", scope);
if i + 1 < scope_count {
base.push_str("%20");
}
}
if let Some(state) = state {
let _ = write!(base, "&state={}", state);
}
base
}
|