package discord import ( "encoding/json" "errors" "fmt" "net/http" "net/url" "strings" "arimelody-web/global" ) const API_ENDPOINT = "https://discord.com/api/v10" var CREDENTIALS_PROVIDED = true var CLIENT_ID = func() string { id := global.Config.Discord.ClientID if id == "" { fmt.Printf("WARN: discord.client_id was not provided. Admin login will be unavailable.\n") CREDENTIALS_PROVIDED = false } return id }() var CLIENT_SECRET = func() string { secret := global.Config.Discord.Secret if secret == "" { fmt.Printf("WARN: discord.secret not provided. Admin login will be unavailable.\n") CREDENTIALS_PROVIDED = false } return secret }() var OAUTH_CALLBACK_URI = fmt.Sprintf("%s/admin/login", global.Config.BaseUrl) var REDIRECT_URI = fmt.Sprintf("https://discord.com/oauth2/authorize?client_id=%s&response_type=code&redirect_uri=%s&scope=identify", CLIENT_ID, OAUTH_CALLBACK_URI) type ( AccessTokenResponse struct { AccessToken string `json:"access_token"` TokenType string `json:"token_type"` ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` Scope string `json:"scope"` } AuthInfoResponse struct { Application struct { ID string `json:"id"` Name string `json:"name"` Icon string `json:"icon"` Description string `json:"description"` Hook bool `json:"hook"` BotPublic bool `json:"bot_public"` BotRequireCodeGrant bool `json:"bot_require_code_grant"` VerifyKey string `json:"verify_key"` } `json:"application"` Scopes []string `json:"scopes"` Expires string `json:"expires"` User DiscordUser `json:"user"` } DiscordUser struct { ID string `json:"id"` Username string `json:"username"` Avatar string `json:"avatar"` Discriminator string `json:"discriminator"` GlobalName string `json:"global_name"` PublicFlags int `json:"public_flags"` } ) func GetOAuthTokenFromCode(code string) (string, error) { // let's get an oauth token! req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s/oauth2/token", API_ENDPOINT), strings.NewReader(url.Values{ "client_id": {CLIENT_ID}, "client_secret": {CLIENT_SECRET}, "grant_type": {"authorization_code"}, "code": {code}, "redirect_uri": {OAUTH_CALLBACK_URI}, }.Encode())) req.Header.Add("Content-Type", "application/x-www-form-urlencoded") res, err := http.DefaultClient.Do(req) if err != nil { return "", errors.New(fmt.Sprintf("Failed while contacting discord API: %s", err)) } oauth := AccessTokenResponse{} err = json.NewDecoder(res.Body).Decode(&oauth) if err != nil { return "", errors.New(fmt.Sprintf("Failed to parse OAuth response data from discord: %s\n", err)) } res.Body.Close() return oauth.AccessToken, nil } func GetDiscordUserFromAuth(token string) (DiscordUser, error) { // let's get authorisation information! req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/oauth2/@me", API_ENDPOINT), nil) req.Header.Add("Authorization", "Bearer " + token) res, err := http.DefaultClient.Do(req) if err != nil { return DiscordUser{}, errors.New(fmt.Sprintf("Failed to retrieve discord auth information: %s\n", err)) } auth_info := AuthInfoResponse{} err = json.NewDecoder(res.Body).Decode(&auth_info) if err != nil { return DiscordUser{}, errors.New(fmt.Sprintf("Failed to parse auth information from discord: %s\n", err)) } defer res.Body.Close() return auth_info.User, nil }