2024-03-04 18:14:38 -05:00
|
|
|
|
package auth
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/url"
|
|
|
|
|
|
|
|
|
|
"github.com/google/go-querystring/query"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type AuthorizeParams struct {
|
|
|
|
|
// A string-encoded JSON object that specifies the claims to include in the ID token.
|
|
|
|
|
// For information about claims, see Requesting claims: https://dev.twitch.tv/docs/authentication/getting-tokens-oidc/#requesting-claims
|
|
|
|
|
//
|
|
|
|
|
// Only used for OIDC auth flows
|
2024-03-05 22:29:23 -05:00
|
|
|
|
Claims *Claims `url:"claims,omitempty"`
|
2024-03-04 18:14:38 -05:00
|
|
|
|
|
|
|
|
|
// Set to true to force the user to re-authorize your app’s access to their resources.
|
|
|
|
|
// The default is false.
|
2024-03-05 22:29:23 -05:00
|
|
|
|
ForceVerify *bool `url:"force_verify,omitempty"`
|
2024-03-04 18:14:38 -05:00
|
|
|
|
|
|
|
|
|
// Although optional, you are strongly encouraged to pass a nonce string to help
|
|
|
|
|
// prevent Cross-Site Request Forgery (CSRF) attacks. The server returns this string
|
|
|
|
|
// to you in the ID token’s list of claims. If this string doesn’t match the nonce
|
|
|
|
|
// string that you passed, ignore the response. The nonce string should be randomly
|
|
|
|
|
// generated and unique for each OAuth request.
|
|
|
|
|
//
|
|
|
|
|
// Only used for OIDC auth flows
|
2024-03-05 22:29:23 -05:00
|
|
|
|
Nonce *string `url:"nonce,omitempty"`
|
2024-03-04 18:14:38 -05:00
|
|
|
|
|
|
|
|
|
// Must be set to code for Authorization code grant flow.
|
|
|
|
|
// Recommended for Server-to-Server flows. (with backend)
|
|
|
|
|
//
|
|
|
|
|
// Must be set to token for Implicit grant flow.
|
|
|
|
|
// Recommended for Client-to-Server flows. (no backend)
|
|
|
|
|
ResponseType string `url:"response_type"`
|
|
|
|
|
|
|
|
|
|
// A space-delimited list of scopes. The APIs that you’re calling identify the
|
|
|
|
|
// scopes you must list. The list must include the openid scope. Don’t forget to
|
|
|
|
|
// URL encode the list.
|
|
|
|
|
Scope []Scope `url:"scope,space"`
|
|
|
|
|
|
|
|
|
|
// Although optional, you are strongly encouraged to pass a state string to help
|
|
|
|
|
// prevent Cross-Site Request Forgery (CSRF) attacks. The server returns this string
|
|
|
|
|
// to you in your redirect URI (see the state parameter in the fragment portion of
|
|
|
|
|
// the URI). If this string doesn’t match the state string that you passed, ignore
|
|
|
|
|
// the response. The state string should be randomly generated and unique for each
|
|
|
|
|
// OAuth request.
|
2024-03-05 22:29:23 -05:00
|
|
|
|
State *string `url:"state,omitempty"`
|
2024-03-04 18:14:38 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const AuthorizeUrl = "https://id.twitch.tv/oauth2/authorize"
|
|
|
|
|
|
|
|
|
|
// AuthorizeUrl returns the URL to redirect the user to for authorization.
|
2024-03-08 13:08:54 -05:00
|
|
|
|
func (c *Auth) AuthorizeUrl(params *AuthorizeParams) *url.URL {
|
2024-03-04 18:14:38 -05:00
|
|
|
|
v, _ := query.Values(params)
|
|
|
|
|
v.Set("client_id", c.clientId)
|
|
|
|
|
v.Set("redirect_uri", c.redirectUri)
|
|
|
|
|
url, _ := url.Parse(AuthorizeUrl)
|
|
|
|
|
url.RawQuery = v.Encode()
|
|
|
|
|
return url
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type AuthorizeHandler struct {
|
2024-03-08 13:08:54 -05:00
|
|
|
|
client *Auth
|
2024-03-04 18:14:38 -05:00
|
|
|
|
scopes []Scope
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ http.Handler = (*AuthorizeHandler)(nil)
|
|
|
|
|
|
|
|
|
|
// AuthorizeHandler returns an http.Handler that redirects the user to the
|
|
|
|
|
// authorization URL.
|
2024-03-08 13:08:54 -05:00
|
|
|
|
func (c *Auth) AuthorizeHandler(scopes []Scope) http.Handler {
|
2024-03-04 18:14:38 -05:00
|
|
|
|
return &AuthorizeHandler{
|
|
|
|
|
client: c,
|
|
|
|
|
scopes: scopes,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ServeHTTP implements http.Handler.
|
|
|
|
|
func (h *AuthorizeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
state := GenerateState()
|
|
|
|
|
|
|
|
|
|
if err := h.client.stateStorage.Save(w, state); err != nil {
|
|
|
|
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
url := h.client.AuthorizeUrl(&AuthorizeParams{
|
|
|
|
|
ResponseType: "code",
|
|
|
|
|
Scope: h.scopes,
|
|
|
|
|
State: &state,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
http.Redirect(w, r, url.String(), http.StatusFound)
|
|
|
|
|
}
|