2022-07-07 00:19:05 +03:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
2023-03-01 23:29:45 +02:00
|
|
|
"context"
|
2024-09-29 19:23:19 +03:00
|
|
|
"encoding/json"
|
2022-07-07 00:19:05 +03:00
|
|
|
"errors"
|
|
|
|
"net/http"
|
|
|
|
|
2023-11-27 20:32:28 +02:00
|
|
|
"github.com/pocketbase/pocketbase/tools/types"
|
2022-07-07 00:19:05 +03:00
|
|
|
"golang.org/x/oauth2"
|
|
|
|
)
|
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// ProviderFactoryFunc defines a function for initializing a new OAuth2 provider.
|
|
|
|
type ProviderFactoryFunc func() Provider
|
|
|
|
|
|
|
|
// Providers defines a map with all of the available OAuth2 providers.
|
|
|
|
//
|
|
|
|
// To register a new provider append a new entry in the map.
|
|
|
|
var Providers = map[string]ProviderFactoryFunc{}
|
|
|
|
|
|
|
|
// NewProviderByName returns a new preconfigured provider instance by its name identifier.
|
|
|
|
func NewProviderByName(name string) (Provider, error) {
|
|
|
|
factory, ok := Providers[name]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("missing provider " + name)
|
|
|
|
}
|
|
|
|
|
|
|
|
return factory(), nil
|
2022-07-07 00:19:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Provider defines a common interface for an OAuth2 client.
|
|
|
|
type Provider interface {
|
2024-01-23 20:56:14 +02:00
|
|
|
// Context returns the context associated with the provider (if any).
|
2023-03-01 23:29:45 +02:00
|
|
|
Context() context.Context
|
|
|
|
|
|
|
|
// SetContext assigns the specified context to the current provider.
|
|
|
|
SetContext(ctx context.Context)
|
|
|
|
|
2023-11-29 20:19:54 +02:00
|
|
|
// PKCE indicates whether the provider can use the PKCE flow.
|
|
|
|
PKCE() bool
|
|
|
|
|
|
|
|
// SetPKCE toggles the state whether the provider can use the PKCE flow or not.
|
|
|
|
SetPKCE(enable bool)
|
|
|
|
|
|
|
|
// DisplayName usually returns provider name as it is officially written
|
|
|
|
// and it could be used directly in the UI.
|
|
|
|
DisplayName() string
|
|
|
|
|
|
|
|
// SetDisplayName sets the provider's display name.
|
|
|
|
SetDisplayName(displayName string)
|
|
|
|
|
2022-07-07 00:19:05 +03:00
|
|
|
// Scopes returns the provider access permissions that will be requested.
|
|
|
|
Scopes() []string
|
|
|
|
|
|
|
|
// SetScopes sets the provider access permissions that will be requested later.
|
|
|
|
SetScopes(scopes []string)
|
|
|
|
|
|
|
|
// ClientId returns the provider client's app ID.
|
|
|
|
ClientId() string
|
|
|
|
|
|
|
|
// SetClientId sets the provider client's ID.
|
|
|
|
SetClientId(clientId string)
|
|
|
|
|
2022-07-10 14:13:44 +08:00
|
|
|
// ClientSecret returns the provider client's app secret.
|
2022-07-07 00:19:05 +03:00
|
|
|
ClientSecret() string
|
|
|
|
|
|
|
|
// SetClientSecret sets the provider client's app secret.
|
|
|
|
SetClientSecret(secret string)
|
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// RedirectURL returns the end address to redirect the user
|
2022-07-07 00:19:05 +03:00
|
|
|
// going through the OAuth flow.
|
2024-09-29 19:23:19 +03:00
|
|
|
RedirectURL() string
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// SetRedirectURL sets the provider's RedirectURL.
|
|
|
|
SetRedirectURL(url string)
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// AuthURL returns the provider's authorization service url.
|
|
|
|
AuthURL() string
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// SetAuthURL sets the provider's AuthURL.
|
|
|
|
SetAuthURL(url string)
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// TokenURL returns the provider's token exchange service url.
|
|
|
|
TokenURL() string
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// SetTokenURL sets the provider's TokenURL.
|
|
|
|
SetTokenURL(url string)
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// UserInfoURL returns the provider's user info api url.
|
|
|
|
UserInfoURL() string
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// SetUserInfoURL sets the provider's UserInfoURL.
|
|
|
|
SetUserInfoURL(url string)
|
2022-07-07 00:19:05 +03:00
|
|
|
|
|
|
|
// Client returns an http client using the provided token.
|
|
|
|
Client(token *oauth2.Token) *http.Client
|
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// BuildAuthURL returns a URL to the provider's consent page
|
2022-07-07 00:19:05 +03:00
|
|
|
// that asks for permissions for the required scopes explicitly.
|
2024-09-29 19:23:19 +03:00
|
|
|
BuildAuthURL(state string, opts ...oauth2.AuthCodeOption) string
|
2022-07-07 00:19:05 +03:00
|
|
|
|
|
|
|
// FetchToken converts an authorization code to token.
|
|
|
|
FetchToken(code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error)
|
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// FetchRawUserInfo requests and marshalizes into `result` the
|
2022-07-07 00:19:05 +03:00
|
|
|
// the OAuth user api response.
|
2024-09-29 19:23:19 +03:00
|
|
|
FetchRawUserInfo(token *oauth2.Token) ([]byte, error)
|
2022-07-07 00:19:05 +03:00
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// FetchAuthUser is similar to FetchRawUserInfo, but normalizes and
|
2022-07-07 00:19:05 +03:00
|
|
|
// marshalizes the user api response into a standardized AuthUser struct.
|
|
|
|
FetchAuthUser(token *oauth2.Token) (user *AuthUser, err error)
|
|
|
|
}
|
|
|
|
|
2024-09-29 19:23:19 +03:00
|
|
|
// wrapFactory is a helper that wraps a Provider specific factory
|
|
|
|
// function and returns its result as Provider interface.
|
|
|
|
func wrapFactory[T Provider](factory func() T) ProviderFactoryFunc {
|
|
|
|
return func() Provider {
|
|
|
|
return factory()
|
2022-07-07 00:19:05 +03:00
|
|
|
}
|
|
|
|
}
|
2024-09-29 19:23:19 +03:00
|
|
|
|
|
|
|
// AuthUser defines a standardized OAuth2 user data structure.
|
|
|
|
type AuthUser struct {
|
|
|
|
Expiry types.DateTime `json:"expiry"`
|
|
|
|
RawUser map[string]any `json:"rawUser"`
|
|
|
|
Id string `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Username string `json:"username"`
|
|
|
|
Email string `json:"email"`
|
|
|
|
AvatarURL string `json:"avatarURL"`
|
|
|
|
AccessToken string `json:"accessToken"`
|
|
|
|
RefreshToken string `json:"refreshToken"`
|
|
|
|
|
|
|
|
// @todo
|
|
|
|
// deprecated: use AvatarURL instead
|
|
|
|
// AvatarUrl will be removed after dropping v0.22 support
|
|
|
|
AvatarUrl string `json:"avatarUrl"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalJSON implements the [json.Marshaler] interface.
|
|
|
|
//
|
|
|
|
// @todo remove after dropping v0.22 support
|
|
|
|
func (au AuthUser) MarshalJSON() ([]byte, error) {
|
|
|
|
type alias AuthUser // prevent recursion
|
|
|
|
|
|
|
|
au2 := alias(au)
|
2024-10-10 14:46:22 +03:00
|
|
|
au2.AvatarUrl = au.AvatarURL // ensure that the legacy field is populated
|
2024-09-29 19:23:19 +03:00
|
|
|
|
|
|
|
return json.Marshal(au2)
|
|
|
|
}
|