aboutsummaryrefslogtreecommitdiff
path: root/src/utils.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils.rs')
-rw-r--r--src/utils.rs130
1 files changed, 130 insertions, 0 deletions
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..e11a313
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,130 @@
+//! 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::Permissions;
+
+use 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
+/// extern crate serenity_model;
+/// extern crate serenity_oauth;
+///
+/// # fn main() {
+/// use serenity_model::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
+}