123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- // Copyright 2024 The Gogs Authors. All rights reserved.
- // Use of this source code is governed by a MIT-style
- // license that can be found in the LICENSE file.
- package auth0
- import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "strings"
- "golang.org/x/oauth2"
- )
- // Config contains configuration for Auth0 authentication provider.
- type Config struct {
- // Domain is the Auth0 domain (e.g., "your-domain.auth0.com")
- Domain string
- // ClientID is the Auth0 application client ID
- ClientID string
- // ClientSecret is the Auth0 application client secret
- ClientSecret string
- // CallbackURL is the callback URL for OAuth2 flow
- CallbackURL string
- // Scopes are the OAuth2 scopes to request
- Scopes []string
- }
- // OAuth2Config returns the OAuth2 configuration for Auth0
- func (c *Config) OAuth2Config() *oauth2.Config {
- return &oauth2.Config{
- ClientID: c.ClientID,
- ClientSecret: c.ClientSecret,
- RedirectURL: c.CallbackURL,
- Scopes: c.Scopes,
- Endpoint: oauth2.Endpoint{
- AuthURL: fmt.Sprintf("https://%s/authorize", c.Domain),
- TokenURL: fmt.Sprintf("https://%s/oauth/token", c.Domain),
- },
- }
- }
- // AuthCodeURL generates the Auth0 authorization URL
- func (c *Config) AuthCodeURL(state string) string {
- config := c.OAuth2Config()
- return config.AuthCodeURL(state, oauth2.AccessTypeOffline)
- }
- // ExchangeCode exchanges authorization code for access token
- func (c *Config) ExchangeCode(ctx context.Context, code string) (*oauth2.Token, error) {
- config := c.OAuth2Config()
- return config.Exchange(ctx, code)
- }
- // UserInfo represents Auth0 user information
- type UserInfo struct {
- Sub string `json:"sub"`
- Name string `json:"name"`
- GivenName string `json:"given_name"`
- FamilyName string `json:"family_name"`
- Picture string `json:"picture"`
- Email string `json:"email"`
- EmailVerified bool `json:"email_verified"`
- Locale string `json:"locale"`
- UpdatedAt string `json:"updated_at"`
- }
- // GetUserInfo retrieves user information from Auth0 using access token
- func (c *Config) GetUserInfo(ctx context.Context, token *oauth2.Token) (*UserInfo, error) {
- client := c.OAuth2Config().Client(ctx, token)
-
- resp, err := client.Get(fmt.Sprintf("https://%s/userinfo", c.Domain))
- if err != nil {
- return nil, fmt.Errorf("failed to get user info: %w", err)
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- body, _ := io.ReadAll(resp.Body)
- return nil, fmt.Errorf("failed to get user info: status %d, body: %s", resp.StatusCode, string(body))
- }
- var userInfo UserInfo
- if err := json.NewDecoder(resp.Body).Decode(&userInfo); err != nil {
- return nil, fmt.Errorf("failed to decode user info: %w", err)
- }
- return &userInfo, nil
- }
- // ValidateConfig validates the Auth0 configuration
- func (c *Config) ValidateConfig() error {
- if c.Domain == "" {
- return fmt.Errorf("Auth0 domain is required")
- }
- if c.ClientID == "" {
- return fmt.Errorf("Auth0 client ID is required")
- }
- if c.ClientSecret == "" {
- return fmt.Errorf("Auth0 client secret is required")
- }
- if c.CallbackURL == "" {
- return fmt.Errorf("Auth0 callback URL is required")
- }
-
- // Validate domain format
- if !strings.HasSuffix(c.Domain, ".auth0.com") && !strings.Contains(c.Domain, ".") {
- return fmt.Errorf("invalid Auth0 domain format")
- }
-
- // Validate callback URL format
- if _, err := url.Parse(c.CallbackURL); err != nil {
- return fmt.Errorf("invalid callback URL: %w", err)
- }
-
- return nil
- }
- // DefaultScopes returns the default OAuth2 scopes for Auth0
- func DefaultScopes() []string {
- return []string{"openid", "profile", "email"}
- }
|