Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
realTristan committed Sep 2, 2022
0 parents commit d242815
Show file tree
Hide file tree
Showing 9 changed files with 397 additions and 0 deletions.
9 changes: 9 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
MIT License

Copyright (c) 2022 Tristan Simpson

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# DisGOAuth ![Stars](https://img.shields.io/github/stars/realTristan/DisGOAuth?color=brightgreen) ![Watchers](https://img.shields.io/github/watchers/realTristan/DisGOAuth?label=Watchers)
![banner](https://user-images.githubusercontent.com/75189508/188035581-008c64d0-d59a-4a95-9e75-55cb3d8f4e79.png)

# About
- DisGOAuth is a light-weight, fast and easy-to-use module that makes using Discord's OAuth2.0 much easier. DisGOAuth uses solely native golang packages which makes it fast and secure.

# Installation
`go get -u github.com/realTristan/DisGOAuth`

# Quick Usage
```go

package main

// Import Packages
import (
"fmt"
"net/http"

// Import DisGOAuth
discord "github.com/realTristan/DisGOAuth"
)

// Main function
func main() {
// Establish a new discord client
var dc *discord.DiscordClient = discord.Init(&discord.DiscordClient{
ClientID: "CLIENT ID",
ClientSecret: "CLIENT SECRET",
RedirectURI: "localhost:8000/redirect",
Scopes: []string{discord.ScopeIdentify},
})

// Home Page Handler
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Send the user to the discord authentication
// website. This is where they authorize access.
dc.RedirectHandler(w, r, "")
})

// The OAuth URL Redirect Uri
http.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
// Put this in the handler of the dc.RedirectURI
// Define Variables
var (
// Get the code from the redirect parameters (&code=...)
codeFromURLParamaters = r.URL.Query()["code"][0]

// Get the access token using the above codeFromURLParamaters
accessToken string = dc.GetAccessToken(codeFromURLParamaters)

// Get the authorized user's data using the above accessToken
userData map[string]interface{} = discord.GetUserData(accessToken)
)
// Print the user data map
fmt.Println(userData)
})

// Listen and Serve to the incoming http requests
http.ListenAndServe(":8000", nil)
}
```

# License
MIT License

Copyright (c) 2022 Tristan Simpson

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
57 changes: 57 additions & 0 deletions access_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package DisGOAuth

// Import Packages
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)

// The GetAccessTokenBody() function is used to return
// the request body bytes being used in the
// GetAccessToken() http request
func (dc *DiscordClient) GetAccessTokenBody(code string) *bytes.Buffer {
return bytes.NewBuffer([]byte(fmt.Sprintf(
"client_id=%s&client_secret=%s&grant_type=authorization_code&redirect_uri=%s&code=%s&scope=identify",
dc.ClientID,
dc.ClientSecret,
dc.RedirectURI,
code,
)))
}

// The GetAccessToken() function is used to send an api
// request to discord's oauth2/token endpoint.
// The function returns the token required for
// accessing the authorized users data
func (dc *DiscordClient) GetAccessToken(code string) string {
// Establish a new request object
var req, _ = http.NewRequest("POST",
"https://discordapp.com/api/oauth2/token",
dc.GetAccessTokenBody(code),
)
// Set the request object's headers
req.Header = http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
"Accept": []string{"application/json"},
}
// Send the http request using the above request object
// then decode the response body and marshal it to
// a readable golang map. Return the "access_token" value
// from said golang map
var (
// Send the http request
res, _ = RequestClient.Do(req)
// Readable golang map used for storing
// the response body
data map[string]interface{}
)
// Decode the response body and return
// the access token
json.NewDecoder(res.Body).Decode(&data)

// Return the token type and the access token
// combined together
return data["token_type"].(string) + " " + data["access_token"].(string)
}
105 changes: 105 additions & 0 deletions discord_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package DisGOAuth

// Import Packages
import (
"fmt"
"net/http"
)

// Request Client for sending http requests
var RequestClient *http.Client = &http.Client{}

// The DiscordClient struct contains five primary keys
/* ClientID: string { "Your Application's Client ID" } */
/* ClientSecret: string { "Your Application's Client Secret" } */
/* Scopes: []string { "Your Application's Permission Scopes (REQUIRED)" } */
/* RedirectURI: string { "The Redirect URI (This is where you use the GetUserData functions)" } */
/* OAuthURL: string { "Your Application's OAuth URL (If none is provided, one will be generated for you)" } */
type DiscordClient struct {
ClientID string
ClientSecret string
RedirectURI string
Scopes []string
OAuthURL string
}

// The AppendScopes() function is used to append
// the provided scopes to the OAuth URL. This function
// is called from the InitOAuthURL() function and is
// only ran if the number of provided scopes is valid.
func (dc *DiscordClient) AppendScopes() {
// Append the initial parameter name (scope)
dc.OAuthURL += "&scope="

// For each of the discord client's scopes
for i := 0; i < len(dc.Scopes); i++ {
// Append the scope to the OAuth URL
dc.OAuthURL += dc.Scopes[i]

// If there are multiple scopes and the
// current index isn't the last scope
if i != len(dc.Scopes) {
// Append %20 (space)
dc.OAuthURL += "%20"
}
}
}

// The InitOAuthURL() function is used to initialize
// a discord OAuth URL. This function is called from
// the Init() function and is only ran if there is
// no previously provided OAuth URL.
func (dc *DiscordClient) InitOAuthURL() {
// Set the OAuth URL to a formatted string
// that contains the client id, redirect uri,
// and response type.
dc.OAuthURL = fmt.Sprintf(
"https://discord.com/api/oauth2/authorize?client_id=%s&redirect_uri=%s&response_type=code&prompt=consent",
dc.ClientID,
dc.RedirectURI,
)
// Append the scopes to the OAuth URL
dc.AppendScopes()
}

// The CheckStructErrors() function is used to check for
// any invalid / empty struct values that are required
// for the discord oauth to work.
func (dc *DiscordClient) CheckStructErrors() {
// Make sure the user has provided
// a valid client id
if len(dc.ClientID) < 1 {
panic("DisGOAuth Error: invalid ClientID in DiscordClient (ClientID: string)")
}
// Make sure the user has provided
// a valid client secret
if len(dc.ClientSecret) < 1 {
panic("DisGOAuth Error: invalid ClientSecret in DiscordClient (ClientSecret: string)")
}
// Make sure the user has provided
// a valid redirect uri
if len(dc.RedirectURI) < 1 {
panic("DisGOAuth Error: invalid RedirectURI in DiscordClient (RedirectURI: string)")
}
// Make sure the user has provided
// a sufficient number of scopes
if len(dc.Scopes) < 1 {
panic("DisGOAuth Error: not enough scopes in DiscordClient (Scopes: []string)")
}
}

// The Init() function is used to initalize
// the required data for the discord oauth to work
// It panics if required parameters are missing from
// the provided DiscordClient struct
func Init(dc *DiscordClient) *DiscordClient {
// Check for DiscordClient struct errors
dc.CheckStructErrors()

// Initialize the OAuth URL
if len(dc.OAuthURL) < 40 {
dc.InitOAuthURL()
}
// Return the discord client
return dc
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/realTristan/DisGOAuth

go 1.17
24 changes: 24 additions & 0 deletions redirect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package DisGOAuth

// Import net/http package
import "net/http"

// The RedirectHandler() function is used to redirect the user
// to the provided DiscordClient OAuth URL. If there is a
// provided state, it will add it to said OAuth URL
//
// If using a state, base64encode the data beforehand, else,
// set the state to "" (length: 0)
func (dc *DiscordClient) RedirectHandler(w http.ResponseWriter, r *http.Request, state string) {
// Create a copy of the OAuth URL
var _url string = dc.OAuthURL

// If the user provided a state. Make sure
// the state is base64 encoded. Or else
// many bugs can arise.
if len(state) > 0 {
_url = dc.OAuthURL + "&state=" + state
}
// Redirect the user to the OAuth URL
http.Redirect(w, r, _url, http.StatusTemporaryRedirect)
}
29 changes: 29 additions & 0 deletions scopes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package DisGOAuth

// Constant OAuth URL Scopes
const (
// Non-Whitelist
ScopeIdentify = "identify"
ScopeBot = "bot"
ScopeEmail = "email"
ScopeGuilds = "guilds"
ScopeGuildsJoin = "guilds.join"
ScopeConnections = "connections"
ScopeGroupDMJoin = "gdm.join"
ScopeMessagesRead = "messages.read"
ScopeWebhookIncoming = "webhook.Incoming"
ScopeApplicationsBuildsRead = "applications.builds.read"
ScopeApplicationsStoreUpdate = "applications.store.update"
ScopeApplicationsEntitlements = "applications.entitlements"
ScopeApplicationsCommands = "applications.commands"
ScopeApplicationsCommandsUpdate = "applications.commands.update"

// Whitelist Only
ScopeRPC = "rpc"
ScopeRPCAPI = "rpc.api"
ScopeRPCNotificationsRead = "rpc.notifications.read"
ScopeApplicationsBuildsUpload = "applications.builds.upload"
ScopeRelationshipsRead = "relationships.read"
ScopeActivitiesRead = "activities.read"
ScopeActivitiesWrite = "activities.write"
)
63 changes: 63 additions & 0 deletions testing/testing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

// Import Packages
import (
"fmt"
"net/http"

// Import DisGOAuth
discord "github.com/realTristan/DisGOAuth"
)

// Main function
func main() {
// Establish a new discord client
var dc *discord.DiscordClient = discord.Init(&discord.DiscordClient{
ClientID: "CLIENT ID",
ClientSecret: "CLIENT SECRET",
RedirectURI: "localhost:8000/redirect",
Scopes: []string{discord.ScopeIdentify},
})

////////////////////////////////////////////////////////////////////////
//
// Home Page Handler
//
// It is suggested to put this in it's own function,
// I only did it like for the showcase.
//
////////////////////////////////////////////////////////////////////////
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// Send the user to the discord authentication
// website. This is where they authorize access.
dc.RedirectHandler(w, r, "")
})

////////////////////////////////////////////////////////////////////////
//
// The OAuth URL Redirect Uri
//
// It is suggested to put this in it's own function,
// I only did it like for the showcase.
//
////////////////////////////////////////////////////////////////////////
http.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
// Put this in the handler of the dc.RedirectURI
// Define Variables
var (
// Get the code from the redirect parameters (&code=...)
codeFromURLParamaters = r.URL.Query()["code"][0]

// Get the access token using the above codeFromURLParamaters
accessToken string = dc.GetAccessToken(codeFromURLParamaters)

// Get the authorized user's data using the above accessToken
userData map[string]interface{} = discord.GetUserData(accessToken)
)
// Print the user data map
fmt.Println(userData)
})

// Listen and Serve to the incoming http requests
http.ListenAndServe(":8000", nil)
}
Loading

0 comments on commit d242815

Please sign in to comment.