mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 04:35:40 +00:00
wip
This commit is contained in:
@@ -38,7 +38,7 @@ COPY --from=site-builder /site/dist ./internal/assets/dist
|
||||
RUN go build
|
||||
|
||||
# Runner
|
||||
FROM busybox:1.37-musl AS runner
|
||||
FROM alpine:3.21 AS runner
|
||||
|
||||
WORKDIR /tinyauth
|
||||
|
||||
|
||||
33
cmd/root.go
33
cmd/root.go
@@ -7,6 +7,7 @@ import (
|
||||
"tinyauth/internal/api"
|
||||
"tinyauth/internal/auth"
|
||||
"tinyauth/internal/hooks"
|
||||
"tinyauth/internal/providers"
|
||||
"tinyauth/internal/types"
|
||||
"tinyauth/internal/utils"
|
||||
|
||||
@@ -58,11 +59,27 @@ var rootCmd = &cobra.Command{
|
||||
users, parseErr := utils.ParseUsers(usersString)
|
||||
HandleError(parseErr, "Failed to parse users")
|
||||
|
||||
// Create OAuth config
|
||||
oauthConfig := types.OAuthConfig{
|
||||
GithubClientId: config.GithubClientId,
|
||||
GithubClientSecret: config.GithubClientSecret,
|
||||
GoogleClientId: config.GoogleClientId,
|
||||
GoogleClientSecret: config.GoogleClientSecret,
|
||||
MicrosoftClientId: config.MicrosoftClientId,
|
||||
MicrosoftClientSecret: config.MicrosoftClientSecret,
|
||||
}
|
||||
|
||||
// Create auth service
|
||||
auth := auth.NewAuth(users)
|
||||
|
||||
// Create OAuth providers service
|
||||
providers := providers.NewProviders(oauthConfig)
|
||||
|
||||
// Initialize providers
|
||||
providers.Init()
|
||||
|
||||
// Create hooks service
|
||||
hooks := hooks.NewHooks(auth)
|
||||
hooks := hooks.NewHooks(auth, providers)
|
||||
|
||||
// Create API
|
||||
api := api.NewAPI(types.APIConfig{
|
||||
@@ -71,7 +88,7 @@ var rootCmd = &cobra.Command{
|
||||
Secret: config.Secret,
|
||||
AppURL: config.AppURL,
|
||||
CookieSecure: config.CookieSecure,
|
||||
}, hooks, auth)
|
||||
}, hooks, auth, providers)
|
||||
|
||||
// Setup routes
|
||||
api.Init()
|
||||
@@ -107,6 +124,12 @@ func init() {
|
||||
rootCmd.Flags().String("users", "", "Comma separated list of users in the format username:bcrypt-hashed-password.")
|
||||
rootCmd.Flags().String("users-file", "", "Path to a file containing users in the format username:bcrypt-hashed-password.")
|
||||
rootCmd.Flags().Bool("cookie-secure", false, "Send cookie over secure connection only.")
|
||||
rootCmd.Flags().String("github-client-id", "", "Github OAuth client ID.")
|
||||
rootCmd.Flags().String("github-client-secret", "", "Github OAuth client secret.")
|
||||
rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.")
|
||||
rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.")
|
||||
rootCmd.Flags().String("microsoft-client-id", "", "Microsoft OAuth client ID.")
|
||||
rootCmd.Flags().String("microsoft-client-secret", "", "Microsoft OAuth client secret.")
|
||||
viper.BindEnv("port", "PORT")
|
||||
viper.BindEnv("address", "ADDRESS")
|
||||
viper.BindEnv("secret", "SECRET")
|
||||
@@ -114,5 +137,11 @@ func init() {
|
||||
viper.BindEnv("users", "USERS")
|
||||
viper.BindEnv("users-file", "USERS_FILE")
|
||||
viper.BindEnv("cookie-secure", "COOKIE_SECURE")
|
||||
viper.BindEnv("github-client-id", "GITHUB_CLIENT_ID")
|
||||
viper.BindEnv("github-client-secret", "GITHUB_CLIENT_SECRET")
|
||||
viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID")
|
||||
viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET")
|
||||
viper.BindEnv("microsoft-client-id", "MICROSOFT_CLIENT_ID")
|
||||
viper.BindEnv("microsoft-client-secret", "MICROSOFT_CLIENT_SECRET")
|
||||
viper.BindPFlags(rootCmd.Flags())
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@@ -72,6 +72,7 @@ require (
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@@ -180,6 +180,8 @@ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjs
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"tinyauth/internal/assets"
|
||||
"tinyauth/internal/auth"
|
||||
"tinyauth/internal/hooks"
|
||||
"tinyauth/internal/providers"
|
||||
"tinyauth/internal/types"
|
||||
"tinyauth/internal/utils"
|
||||
|
||||
@@ -20,12 +21,12 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func NewAPI(config types.APIConfig, hooks *hooks.Hooks, auth *auth.Auth) (*API) {
|
||||
func NewAPI(config types.APIConfig, hooks *hooks.Hooks, auth *auth.Auth, providers *providers.Providers) *API {
|
||||
return &API{
|
||||
Config: config,
|
||||
Hooks: hooks,
|
||||
Auth: auth,
|
||||
Router: nil,
|
||||
Providers: providers,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +35,7 @@ type API struct {
|
||||
Router *gin.Engine
|
||||
Hooks *hooks.Hooks
|
||||
Auth *auth.Auth
|
||||
Providers *providers.Providers
|
||||
}
|
||||
|
||||
func (api *API) Init() {
|
||||
@@ -93,7 +95,15 @@ func (api *API) Init() {
|
||||
|
||||
func (api *API) SetupRoutes() {
|
||||
api.Router.GET("/api/auth", func(c *gin.Context) {
|
||||
userContext := api.Hooks.UseUserContext(c)
|
||||
userContext, userContextErr := api.Hooks.UseUserContext(c)
|
||||
|
||||
if userContextErr != nil {
|
||||
c.JSON(500, gin.H{
|
||||
"status": 500,
|
||||
"message": "Internal Server Error",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if userContext.IsLoggedIn {
|
||||
c.JSON(200, gin.H{
|
||||
@@ -134,7 +144,7 @@ func (api *API) SetupRoutes() {
|
||||
return
|
||||
}
|
||||
|
||||
user := api.Auth.GetUser(login.Username)
|
||||
user := api.Auth.GetUser(login.Email)
|
||||
|
||||
if user == nil {
|
||||
c.JSON(401, gin.H{
|
||||
@@ -153,7 +163,8 @@ func (api *API) SetupRoutes() {
|
||||
}
|
||||
|
||||
session := sessions.Default(c)
|
||||
session.Set("tinyauth", user.Username)
|
||||
session.Set("tinyauth_sid", user.Email)
|
||||
session.Set("tinyauth_oauth_provider", "")
|
||||
session.Save()
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
@@ -164,7 +175,8 @@ func (api *API) SetupRoutes() {
|
||||
|
||||
api.Router.POST("/api/logout", func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
session.Delete("tinyauth")
|
||||
session.Delete("tinyauth_sid")
|
||||
session.Delete("tinyauth_oauth_provider")
|
||||
session.Save()
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
@@ -174,14 +186,24 @@ func (api *API) SetupRoutes() {
|
||||
})
|
||||
|
||||
api.Router.GET("/api/status", func(c *gin.Context) {
|
||||
userContext := api.Hooks.UseUserContext(c)
|
||||
userContext, userContextErr := api.Hooks.UseUserContext(c)
|
||||
|
||||
if userContextErr != nil {
|
||||
c.JSON(500, gin.H{
|
||||
"status": 500,
|
||||
"message": "Internal Server Error",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if !userContext.IsLoggedIn {
|
||||
c.JSON(200, gin.H{
|
||||
"status": 200,
|
||||
"message": "Unauthenticated",
|
||||
"username": "",
|
||||
"email": "",
|
||||
"isLoggedIn": false,
|
||||
"oauth": false,
|
||||
"provider": "",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -189,8 +211,10 @@ func (api *API) SetupRoutes() {
|
||||
c.JSON(200, gin.H{
|
||||
"status": 200,
|
||||
"message": "Authenticated",
|
||||
"username": userContext.Username,
|
||||
"isLoggedIn": true,
|
||||
"email": userContext.Email,
|
||||
"isLoggedIn": userContext.IsLoggedIn,
|
||||
"oauth": userContext.OAuth,
|
||||
"provider": userContext.Provider,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -200,8 +224,81 @@ func (api *API) SetupRoutes() {
|
||||
"message": "OK",
|
||||
})
|
||||
})
|
||||
|
||||
api.Router.GET("/api/oauth/url/:provider", func(c *gin.Context) {
|
||||
var provider types.OAuthBind
|
||||
|
||||
bindErr := c.BindUri(&provider)
|
||||
|
||||
if bindErr != nil {
|
||||
c.JSON(400, gin.H{
|
||||
"status": 400,
|
||||
"message": "Bad Request",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
authURL := api.Providers.GetAuthURL(provider.Provider)
|
||||
|
||||
if authURL == "" {
|
||||
c.JSON(400, gin.H{
|
||||
"status": 400,
|
||||
"message": "Bad Request",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"status": 200,
|
||||
"message": "Ok",
|
||||
"url": authURL,
|
||||
})
|
||||
})
|
||||
|
||||
api.Router.GET("/api/oauth/callback/:provider", func(c *gin.Context) {
|
||||
var provider types.OAuthBind
|
||||
|
||||
bindErr := c.BindUri(&provider)
|
||||
|
||||
if bindErr != nil {
|
||||
c.JSON(400, gin.H{
|
||||
"status": 400,
|
||||
"message": "Bad Request",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
code := c.Query("code")
|
||||
|
||||
if code == "" {
|
||||
c.JSON(400, gin.H{
|
||||
"status": 400,
|
||||
"message": "Bad Request",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
email, emailErr := api.Providers.Login(code, provider.Provider)
|
||||
|
||||
if emailErr != nil {
|
||||
c.JSON(500, gin.H{
|
||||
"status": 500,
|
||||
"message": "Internal Server Error",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
session := sessions.Default(c)
|
||||
session.Set("tinyauth_sid", email)
|
||||
session.Set("tinyauth_oauth_provider", provider.Provider)
|
||||
session.Save()
|
||||
|
||||
c.JSON(200, gin.H{
|
||||
"status": 200,
|
||||
"message": "Logged in",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (api *API) Run() {
|
||||
log.Info().Str("address", api.Config.Address).Int("port", api.Config.Port).Msg("Starting server")
|
||||
|
||||
@@ -16,9 +16,9 @@ type Auth struct {
|
||||
Users types.Users
|
||||
}
|
||||
|
||||
func (auth *Auth) GetUser(username string) *types.User {
|
||||
func (auth *Auth) GetUser(email string) *types.User {
|
||||
for _, user := range auth.Users {
|
||||
if user.Username == username {
|
||||
if user.Email == email {
|
||||
return &user
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,53 +2,83 @@ package hooks
|
||||
|
||||
import (
|
||||
"tinyauth/internal/auth"
|
||||
"tinyauth/internal/providers"
|
||||
"tinyauth/internal/types"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewHooks(auth *auth.Auth) *Hooks {
|
||||
func NewHooks(auth *auth.Auth, providers *providers.Providers) *Hooks {
|
||||
return &Hooks{
|
||||
Auth: auth,
|
||||
Providers: providers,
|
||||
}
|
||||
}
|
||||
|
||||
type Hooks struct {
|
||||
Auth *auth.Auth
|
||||
Providers *providers.Providers
|
||||
}
|
||||
|
||||
func (hooks *Hooks) UseUserContext(c *gin.Context) (types.UserContext) {
|
||||
func (hooks *Hooks) UseUserContext(c *gin.Context) (types.UserContext, error) {
|
||||
session := sessions.Default(c)
|
||||
cookie := session.Get("tinyauth")
|
||||
sessionCookie := session.Get("tinyauth_sid")
|
||||
oauthProviderCookie := session.Get("tinyauth_oauth_provider")
|
||||
|
||||
if cookie == nil {
|
||||
if sessionCookie == nil {
|
||||
return types.UserContext{
|
||||
Username: "",
|
||||
Email: "",
|
||||
IsLoggedIn: false,
|
||||
}
|
||||
OAuth: false,
|
||||
Provider: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
username, ok := cookie.(string)
|
||||
email, emailOk := sessionCookie.(string)
|
||||
provider, providerOk := oauthProviderCookie.(string)
|
||||
|
||||
if !ok {
|
||||
if provider == "" || !providerOk {
|
||||
if !emailOk {
|
||||
return types.UserContext{
|
||||
Username: "",
|
||||
Email: "",
|
||||
IsLoggedIn: false,
|
||||
OAuth: false,
|
||||
Provider: "",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
user := hooks.Auth.GetUser(username)
|
||||
|
||||
user := hooks.Auth.GetUser(email)
|
||||
if user == nil {
|
||||
return types.UserContext{
|
||||
Username: "",
|
||||
Email: "",
|
||||
IsLoggedIn: false,
|
||||
OAuth: false,
|
||||
Provider: "",
|
||||
}, nil
|
||||
}
|
||||
return types.UserContext{
|
||||
Email: email,
|
||||
IsLoggedIn: true,
|
||||
OAuth: false,
|
||||
Provider: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
oauthEmail, oauthEmailErr := hooks.Providers.GetUser(provider)
|
||||
|
||||
if oauthEmailErr != nil {
|
||||
return types.UserContext{
|
||||
Email: "",
|
||||
IsLoggedIn: false,
|
||||
OAuth: false,
|
||||
Provider: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return types.UserContext{
|
||||
Username: username,
|
||||
Email: oauthEmail,
|
||||
IsLoggedIn: true,
|
||||
}
|
||||
OAuth: true,
|
||||
Provider: provider,
|
||||
}, nil
|
||||
}
|
||||
45
internal/oauth/oauth.go
Normal file
45
internal/oauth/oauth.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package oauth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
func NewOAuth(config oauth2.Config) *OAuth {
|
||||
return &OAuth{
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
type OAuth struct {
|
||||
Config oauth2.Config
|
||||
Context context.Context
|
||||
Token *oauth2.Token
|
||||
Verifier string
|
||||
}
|
||||
|
||||
func (oauth *OAuth) Init() {
|
||||
oauth.Context = context.Background()
|
||||
oauth.Verifier = oauth2.GenerateVerifier()
|
||||
}
|
||||
|
||||
func (oauth *OAuth) GetAuthURL() string {
|
||||
return oauth.Config.AuthCodeURL("state", oauth2.AccessTypeOffline, oauth2.S256ChallengeOption(oauth.Verifier))
|
||||
}
|
||||
|
||||
func (oauth *OAuth) ExchangeToken(code string) error {
|
||||
token, err := oauth.Config.Exchange(oauth.Context, code, oauth2.VerifierOption(oauth.Verifier))
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to exchange code")
|
||||
return err
|
||||
}
|
||||
oauth.Token = token
|
||||
return nil
|
||||
}
|
||||
|
||||
func (oauth *OAuth) GetClient() *http.Client {
|
||||
return oauth.Config.Client(oauth.Context, oauth.Token)
|
||||
}
|
||||
47
internal/providers/github.go
Normal file
47
internal/providers/github.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type GithubEmailsResponse []struct {
|
||||
Email string `json:"email"`
|
||||
Primary bool `json:"primary"`
|
||||
}
|
||||
|
||||
func GithubScopes() ([]string) {
|
||||
return []string{"user:email"}
|
||||
}
|
||||
|
||||
func GetGithubEmail(client *http.Client) (string, error) {
|
||||
res, resErr := client.Get("https://api.github.com/user/emails")
|
||||
|
||||
if resErr != nil {
|
||||
return "", resErr
|
||||
}
|
||||
|
||||
body, bodyErr := io.ReadAll(res.Body)
|
||||
|
||||
if bodyErr != nil {
|
||||
return "", bodyErr
|
||||
}
|
||||
|
||||
var emails GithubEmailsResponse
|
||||
|
||||
jsonErr := json.Unmarshal(body, &emails)
|
||||
|
||||
if jsonErr != nil {
|
||||
return "", jsonErr
|
||||
}
|
||||
|
||||
for _, email := range emails {
|
||||
if email.Primary {
|
||||
return email.Email, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("no primary email found")
|
||||
}
|
||||
86
internal/providers/providers.go
Normal file
86
internal/providers/providers.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"tinyauth/internal/oauth"
|
||||
"tinyauth/internal/types"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/endpoints"
|
||||
)
|
||||
|
||||
func NewProviders(config types.OAuthConfig) *Providers {
|
||||
return &Providers{
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
type Providers struct {
|
||||
Config types.OAuthConfig
|
||||
Github *oauth.OAuth
|
||||
Google *oauth.OAuth
|
||||
Microsoft *oauth.OAuth
|
||||
}
|
||||
|
||||
func (providers *Providers) Init() {
|
||||
if providers.Config.GithubClientId != "" && providers.Config.GithubClientSecret != "" {
|
||||
log.Info().Msg("Initializing Github OAuth")
|
||||
providers.Github = oauth.NewOAuth(oauth2.Config{
|
||||
ClientID: providers.Config.GithubClientId,
|
||||
ClientSecret: providers.Config.GithubClientSecret,
|
||||
Scopes: GithubScopes(),
|
||||
Endpoint: endpoints.GitHub,
|
||||
})
|
||||
providers.Github.Init()
|
||||
}
|
||||
}
|
||||
|
||||
func (providers *Providers) Login(code string, provider string) (string, error) {
|
||||
switch provider {
|
||||
case "github":
|
||||
if providers.Github == nil {
|
||||
return "", nil
|
||||
}
|
||||
exchangeErr := providers.Github.ExchangeToken(code)
|
||||
if exchangeErr != nil {
|
||||
return "", exchangeErr
|
||||
}
|
||||
client := providers.Github.GetClient()
|
||||
email, emailErr := GetGithubEmail(client)
|
||||
if emailErr != nil {
|
||||
return "", emailErr
|
||||
}
|
||||
return email, nil
|
||||
default:
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (providers *Providers) GetUser(provider string) (string, error) {
|
||||
switch provider {
|
||||
case "github":
|
||||
if providers.Github == nil {
|
||||
return "", nil
|
||||
}
|
||||
client := providers.Github.GetClient()
|
||||
email, emailErr := GetGithubEmail(client)
|
||||
if emailErr != nil {
|
||||
return "", emailErr
|
||||
}
|
||||
return email, nil
|
||||
default:
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
|
||||
func (providers *Providers) GetAuthURL(provider string) string {
|
||||
switch provider {
|
||||
case "github":
|
||||
if providers.Github == nil {
|
||||
return ""
|
||||
}
|
||||
return providers.Github.GetAuthURL()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
package types
|
||||
|
||||
import "tinyauth/internal/oauth"
|
||||
|
||||
type LoginQuery struct {
|
||||
RedirectURI string `url:"redirect_uri"`
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Username string
|
||||
Email string
|
||||
Password string
|
||||
}
|
||||
|
||||
@@ -24,11 +26,19 @@ type Config struct {
|
||||
Users string `mapstructure:"users"`
|
||||
UsersFile string `mapstructure:"users-file"`
|
||||
CookieSecure bool `mapstructure:"cookie-secure"`
|
||||
GithubClientId string `mapstructure:"github-client-id"`
|
||||
GithubClientSecret string `mapstructure:"github-client-secret"`
|
||||
GoogleClientId string `mapstructure:"google-client-id"`
|
||||
GoogleClientSecret string `mapstructure:"google-client-secret"`
|
||||
MicrosoftClientId string `mapstructure:"microsoft-client-id"`
|
||||
MicrosoftClientSecret string `mapstructure:"microsoft-client-secret"`
|
||||
}
|
||||
|
||||
type UserContext struct {
|
||||
Username string
|
||||
Email string
|
||||
IsLoggedIn bool
|
||||
OAuth bool
|
||||
Provider string
|
||||
}
|
||||
|
||||
type APIConfig struct {
|
||||
@@ -38,3 +48,27 @@ type APIConfig struct {
|
||||
AppURL string
|
||||
CookieSecure bool
|
||||
}
|
||||
|
||||
type OAuthConfig struct {
|
||||
GithubClientId string
|
||||
GithubClientSecret string
|
||||
GoogleClientId string
|
||||
GoogleClientSecret string
|
||||
MicrosoftClientId string
|
||||
MicrosoftClientSecret string
|
||||
}
|
||||
|
||||
type OAuthBind struct {
|
||||
Provider string `uri:"provider" binding:"required"`
|
||||
}
|
||||
|
||||
type OAuthProviders struct {
|
||||
Github *oauth.OAuth
|
||||
Google *oauth.OAuth
|
||||
Microsoft *oauth.OAuth
|
||||
}
|
||||
|
||||
type OAuthLogin struct {
|
||||
Email string
|
||||
Token string
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ func ParseUsers(users string) (types.Users, error) {
|
||||
return types.Users{}, errors.New("invalid user format")
|
||||
}
|
||||
usersParsed = append(usersParsed, types.User{
|
||||
Username: userSplit[0],
|
||||
Email: userSplit[0],
|
||||
Password: userSplit[1],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export const LoginPage = () => {
|
||||
}
|
||||
|
||||
const schema = z.object({
|
||||
username: z.string(),
|
||||
email: z.string().email(),
|
||||
password: z.string(),
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ export const LoginPage = () => {
|
||||
const form = useForm({
|
||||
mode: "uncontrolled",
|
||||
initialValues: {
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
},
|
||||
validate: zodResolver(schema),
|
||||
@@ -42,7 +42,7 @@ export const LoginPage = () => {
|
||||
onError: () => {
|
||||
notifications.show({
|
||||
title: "Failed to login",
|
||||
message: "Check your username and password",
|
||||
message: "Check your email and password",
|
||||
color: "red",
|
||||
});
|
||||
},
|
||||
@@ -68,12 +68,12 @@ export const LoginPage = () => {
|
||||
<Paper shadow="md" p={30} mt={30} radius="md" withBorder>
|
||||
<form onSubmit={form.onSubmit(handleSubmit)}>
|
||||
<TextInput
|
||||
label="Username"
|
||||
placeholder="tinyauth"
|
||||
label="Email"
|
||||
placeholder="user@example.com"
|
||||
required
|
||||
disabled={loginMutation.isLoading}
|
||||
key={form.key("username")}
|
||||
{...form.getInputProps("username")}
|
||||
key={form.key("email")}
|
||||
{...form.getInputProps("email")}
|
||||
/>
|
||||
<PasswordInput
|
||||
label="Password"
|
||||
@@ -90,7 +90,7 @@ export const LoginPage = () => {
|
||||
type="submit"
|
||||
loading={loginMutation.isLoading}
|
||||
>
|
||||
Sign in
|
||||
Login
|
||||
</Button>
|
||||
</form>
|
||||
</Paper>
|
||||
|
||||
@@ -5,9 +5,10 @@ import axios from "axios";
|
||||
import { useUserContext } from "../context/user-context";
|
||||
import { Navigate } from "react-router";
|
||||
import { Layout } from "../components/layouts/layout";
|
||||
import { capitalize } from "../utils/utils";
|
||||
|
||||
export const LogoutPage = () => {
|
||||
const { isLoggedIn, username } = useUserContext();
|
||||
const { isLoggedIn, email, oauth, provider } = useUserContext();
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return <Navigate to="/login" />;
|
||||
@@ -43,8 +44,9 @@ export const LogoutPage = () => {
|
||||
Logout
|
||||
</Text>
|
||||
<Text>
|
||||
You are currently logged in as <Code>{username}</Code>, click the
|
||||
button below to log out.
|
||||
You are currently logged in as <Code>{email}</Code>{" "}
|
||||
{oauth && `using ${capitalize(provider)}`}. Click the button below to
|
||||
log out.
|
||||
</Text>
|
||||
<Button
|
||||
fullWidth
|
||||
|
||||
@@ -2,7 +2,9 @@ import { z } from "zod";
|
||||
|
||||
export const userContextSchema = z.object({
|
||||
isLoggedIn: z.boolean(),
|
||||
username: z.string(),
|
||||
email: z.string(),
|
||||
oauth: z.boolean(),
|
||||
provider: z.string(),
|
||||
});
|
||||
|
||||
export type UserContextSchemaType = z.infer<typeof userContextSchema>;
|
||||
|
||||
1
site/src/utils/utils.ts
Normal file
1
site/src/utils/utils.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
|
||||
Reference in New Issue
Block a user