diff options
Diffstat (limited to 'src/utils.rs')
| -rw-r--r-- | src/utils.rs | 130 |
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 +} |