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 Claims *Claims `url:"claims,omitempty"` // Set to true to force the user to re-authorize your app’s access to their resources. // The default is false. ForceVerify *bool `url:"force_verify,omitempty"` // 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 Nonce *string `url:"nonce,omitempty"` // 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. State *string `url:"state,omitempty"` } const AuthorizeUrl = "https://id.twitch.tv/oauth2/authorize" // AuthorizeUrl returns the URL to redirect the user to for authorization. func (c *Client) AuthorizeUrl(params *AuthorizeParams) *url.URL { 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 { client *Client scopes []Scope } var _ http.Handler = (*AuthorizeHandler)(nil) // AuthorizeHandler returns an http.Handler that redirects the user to the // authorization URL. func (c *Client) AuthorizeHandler(scopes []Scope) http.Handler { 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) }