refactor: remove tailscale oauth

This commit is contained in:
Stavros
2025-04-10 15:14:01 +03:00
parent 2242c9c1e6
commit 1169c633cc
10 changed files with 57 additions and 261 deletions

View File

@@ -12,9 +12,6 @@ GITHUB_CLIENT_SECRET_FILE=github_client_secret_file
GOOGLE_CLIENT_ID=google_client_id GOOGLE_CLIENT_ID=google_client_id
GOOGLE_CLIENT_SECRET=google_client_secret GOOGLE_CLIENT_SECRET=google_client_secret
GOOGLE_CLIENT_SECRET_FILE=google_client_secret_file GOOGLE_CLIENT_SECRET_FILE=google_client_secret_file
TAILSCALE_CLIENT_ID=tailscale_client_id
TAILSCALE_CLIENT_SECRET=tailscale_client_secret
TAILSCALE_CLIENT_SECRET_FILE=tailscale__client_secret_file
GENERIC_CLIENT_ID=generic_client_id GENERIC_CLIENT_ID=generic_client_id
GENERIC_CLIENT_SECRET=generic_client_secret GENERIC_CLIENT_SECRET=generic_client_secret
GENERIC_CLIENT_SECRET_FILE=generic_client_secret_file GENERIC_CLIENT_SECRET_FILE=generic_client_secret_file

View File

@@ -42,7 +42,6 @@ var rootCmd = &cobra.Command{
config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile) config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile)
config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile) config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile)
config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile) config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile)
config.TailscaleClientSecret = utils.GetSecret(config.TailscaleClientSecret, config.TailscaleClientSecretFile)
// Validate config // Validate config
validator := validator.New() validator := validator.New()
@@ -77,19 +76,17 @@ var rootCmd = &cobra.Command{
// Create OAuth config // Create OAuth config
oauthConfig := types.OAuthConfig{ oauthConfig := types.OAuthConfig{
GithubClientId: config.GithubClientId, GithubClientId: config.GithubClientId,
GithubClientSecret: config.GithubClientSecret, GithubClientSecret: config.GithubClientSecret,
GoogleClientId: config.GoogleClientId, GoogleClientId: config.GoogleClientId,
GoogleClientSecret: config.GoogleClientSecret, GoogleClientSecret: config.GoogleClientSecret,
TailscaleClientId: config.TailscaleClientId, GenericClientId: config.GenericClientId,
TailscaleClientSecret: config.TailscaleClientSecret, GenericClientSecret: config.GenericClientSecret,
GenericClientId: config.GenericClientId, GenericScopes: strings.Split(config.GenericScopes, ","),
GenericClientSecret: config.GenericClientSecret, GenericAuthURL: config.GenericAuthURL,
GenericScopes: strings.Split(config.GenericScopes, ","), GenericTokenURL: config.GenericTokenURL,
GenericAuthURL: config.GenericAuthURL, GenericUserURL: config.GenericUserURL,
GenericTokenURL: config.GenericTokenURL, AppURL: config.AppURL,
GenericUserURL: config.GenericUserURL,
AppURL: config.AppURL,
} }
// Create handlers config // Create handlers config
@@ -189,9 +186,6 @@ func init() {
rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.") rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.")
rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.") rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.")
rootCmd.Flags().String("google-client-secret-file", "", "Google OAuth client secret file.") rootCmd.Flags().String("google-client-secret-file", "", "Google OAuth client secret file.")
rootCmd.Flags().String("tailscale-client-id", "", "Tailscale OAuth client ID.")
rootCmd.Flags().String("tailscale-client-secret", "", "Tailscale OAuth client secret.")
rootCmd.Flags().String("tailscale-client-secret-file", "", "Tailscale OAuth client secret file.")
rootCmd.Flags().String("generic-client-id", "", "Generic OAuth client ID.") rootCmd.Flags().String("generic-client-id", "", "Generic OAuth client ID.")
rootCmd.Flags().String("generic-client-secret", "", "Generic OAuth client secret.") rootCmd.Flags().String("generic-client-secret", "", "Generic OAuth client secret.")
rootCmd.Flags().String("generic-client-secret-file", "", "Generic OAuth client secret file.") rootCmd.Flags().String("generic-client-secret-file", "", "Generic OAuth client secret file.")
@@ -223,9 +217,6 @@ func init() {
viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID") viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID")
viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET") viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET")
viper.BindEnv("google-client-secret-file", "GOOGLE_CLIENT_SECRET_FILE") viper.BindEnv("google-client-secret-file", "GOOGLE_CLIENT_SECRET_FILE")
viper.BindEnv("tailscale-client-id", "TAILSCALE_CLIENT_ID")
viper.BindEnv("tailscale-client-secret", "TAILSCALE_CLIENT_SECRET")
viper.BindEnv("tailscale-client-secret-file", "TAILSCALE_CLIENT_SECRET_FILE")
viper.BindEnv("generic-client-id", "GENERIC_CLIENT_ID") viper.BindEnv("generic-client-id", "GENERIC_CLIENT_ID")
viper.BindEnv("generic-client-secret", "GENERIC_CLIENT_SECRET") viper.BindEnv("generic-client-secret", "GENERIC_CLIENT_SECRET")
viper.BindEnv("generic-client-secret-file", "GENERIC_CLIENT_SECRET_FILE") viper.BindEnv("generic-client-secret-file", "GENERIC_CLIENT_SECRET_FILE")

View File

@@ -2,7 +2,6 @@ import { Grid, Button } from "@mantine/core";
import { GithubIcon } from "../../icons/github"; import { GithubIcon } from "../../icons/github";
import { GoogleIcon } from "../../icons/google"; import { GoogleIcon } from "../../icons/google";
import { OAuthIcon } from "../../icons/oauth"; import { OAuthIcon } from "../../icons/oauth";
import { TailscaleIcon } from "../../icons/tailscale";
interface OAuthButtonsProps { interface OAuthButtonsProps {
oauthProviders: string[]; oauthProviders: string[];
@@ -41,19 +40,6 @@ export const OAuthButtons = (props: OAuthButtonsProps) => {
</Button> </Button>
</Grid.Col> </Grid.Col>
)} )}
{oauthProviders.includes("tailscale") && (
<Grid.Col span="content">
<Button
radius="xl"
leftSection={<TailscaleIcon style={{ width: 14, height: 14 }} />}
variant="default"
onClick={() => mutate("tailscale")}
loading={isLoading}
>
Tailscale
</Button>
</Grid.Col>
)}
{oauthProviders.includes("generic") && ( {oauthProviders.includes("generic") && (
<Grid.Col span="content"> <Grid.Col span="content">
<Button <Button

View File

@@ -1,27 +0,0 @@
import { useColorScheme } from "@mantine/hooks";
import type { SVGProps } from "react";
export function TailscaleIcon(props: SVGProps<SVGSVGElement>) {
const colorScheme = useColorScheme();
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
width={24}
height={24}
{...props}
>
{colorScheme === "dark" ? (
<>
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 318.1c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9S1.8 219 1.8 254.2s28.6 63.9 63.8 63.9m191.6 0c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m0 193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m189.2-193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" fill="#ffffff"/>
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 127.7c35.3 0 63.9-28.6 63.9-63.9S100.9 0 65.6 0 1.8 28.6 1.8 63.9s28.6 63.8 63.8 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.8 28.7-63.8 63.9S30.4 512 65.6 512m191.6-384.3c35.3 0 63.9-28.6 63.9-63.9S292.5 0 257.2 0s-63.9 28.6-63.9 63.9 28.6 63.8 63.9 63.8m189.2 0c35.3 0 63.9-28.6 63.9-63.9S481.6 0 446.4 0c-35.3 0-63.9 28.6-63.9 63.9s28.6 63.8 63.9 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" fill="#CCCAC9" opacity="0.2"/>
</>
) : (
<>
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 318.1c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9S1.8 219 1.8 254.2s28.6 63.9 63.8 63.9m191.6 0c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m0 193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9m189.2-193.9c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9"/>
<path xmlns="http://www.w3.org/2000/svg" d="M65.6 127.7c35.3 0 63.9-28.6 63.9-63.9S100.9 0 65.6 0 1.8 28.6 1.8 63.9s28.6 63.8 63.8 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.8 28.7-63.8 63.9S30.4 512 65.6 512m191.6-384.3c35.3 0 63.9-28.6 63.9-63.9S292.5 0 257.2 0s-63.9 28.6-63.9 63.9 28.6 63.8 63.9 63.8m189.2 0c35.3 0 63.9-28.6 63.9-63.9S481.6 0 446.4 0c-35.3 0-63.9 28.6-63.9 63.9s28.6 63.8 63.9 63.8m0 384.3c35.3 0 63.9-28.6 63.9-63.9s-28.6-63.9-63.9-63.9-63.9 28.6-63.9 63.9 28.6 63.9 63.9 63.9" opacity=".2"/>
</>
)}
</svg>
);
}

View File

@@ -2,7 +2,6 @@ package handlers
import ( import (
"fmt" "fmt"
"math/rand/v2"
"net/http" "net/http"
"strings" "strings"
"tinyauth/internal/auth" "tinyauth/internal/auth"
@@ -531,32 +530,6 @@ func (h *Handlers) OauthUrlHandler(c *gin.Context) {
}) })
} }
// Tailscale does not have an auth url so we create a random code (does not need to be secure) to avoid caching and send it
if request.Provider == "tailscale" {
// Build tailscale query
queries, err := query.Values(types.TailscaleQuery{
Code: (1000 + rand.IntN(9000)),
})
// Handle error
if err != nil {
log.Error().Err(err).Msg("Failed to build queries")
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
// Return tailscale URL (immidiately redirects to the callback)
c.JSON(200, gin.H{
"status": 200,
"message": "OK",
"url": fmt.Sprintf("%s/api/oauth/callback/tailscale?%s", h.Config.AppURL, queries.Encode()),
})
return
}
// Return auth URL // Return auth URL
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"status": 200, "status": 200,

View File

@@ -17,11 +17,10 @@ func NewProviders(config types.OAuthConfig) *Providers {
} }
type Providers struct { type Providers struct {
Config types.OAuthConfig Config types.OAuthConfig
Github *oauth.OAuth Github *oauth.OAuth
Google *oauth.OAuth Google *oauth.OAuth
Tailscale *oauth.OAuth Generic *oauth.OAuth
Generic *oauth.OAuth
} }
func (providers *Providers) Init() { func (providers *Providers) Init() {
@@ -59,22 +58,6 @@ func (providers *Providers) Init() {
providers.Google.Init() providers.Google.Init()
} }
if providers.Config.TailscaleClientId != "" && providers.Config.TailscaleClientSecret != "" {
log.Info().Msg("Initializing Tailscale OAuth")
// Create a new oauth provider with the tailscale config
providers.Tailscale = oauth.NewOAuth(oauth2.Config{
ClientID: providers.Config.TailscaleClientId,
ClientSecret: providers.Config.TailscaleClientSecret,
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/tailscale", providers.Config.AppURL),
Scopes: TailscaleScopes(),
Endpoint: TailscaleEndpoint,
})
// Initialize the oauth provider
providers.Tailscale.Init()
}
// If we have a client id and secret for generic oauth, initialize the oauth provider // If we have a client id and secret for generic oauth, initialize the oauth provider
if providers.Config.GenericClientId != "" && providers.Config.GenericClientSecret != "" { if providers.Config.GenericClientId != "" && providers.Config.GenericClientSecret != "" {
log.Info().Msg("Initializing Generic OAuth") log.Info().Msg("Initializing Generic OAuth")
@@ -103,8 +86,6 @@ func (providers *Providers) GetProvider(provider string) *oauth.OAuth {
return providers.Github return providers.Github
case "google": case "google":
return providers.Google return providers.Google
case "tailscale":
return providers.Tailscale
case "generic": case "generic":
return providers.Generic return providers.Generic
default: default:
@@ -161,30 +142,6 @@ func (providers *Providers) GetUser(provider string) (string, error) {
log.Debug().Msg("Got email from google") log.Debug().Msg("Got email from google")
// Return the email
return email, nil
case "tailscale":
// If the tailscale provider is not configured, return an error
if providers.Tailscale == nil {
log.Debug().Msg("Tailscale provider not configured")
return "", nil
}
// Get the client from the tailscale provider
client := providers.Tailscale.GetClient()
log.Debug().Msg("Got client from tailscale")
// Get the email from the tailscale provider
email, err := GetTailscaleEmail(client)
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Got email from tailscale")
// Return the email // Return the email
return email, nil return email, nil
case "generic": case "generic":
@@ -225,9 +182,6 @@ func (provider *Providers) GetConfiguredProviders() []string {
if provider.Google != nil { if provider.Google != nil {
providers = append(providers, "google") providers = append(providers, "google")
} }
if provider.Tailscale != nil {
providers = append(providers, "tailscale")
}
if provider.Generic != nil { if provider.Generic != nil {
providers = append(providers, "generic") providers = append(providers, "generic")
} }

View File

@@ -1,68 +0,0 @@
package providers
import (
"encoding/json"
"io"
"net/http"
"github.com/rs/zerolog/log"
"golang.org/x/oauth2"
)
// The tailscale email is the loginName
type TailscaleUser struct {
LoginName string `json:"loginName"`
}
// The response from the tailscale user info endpoint
type TailscaleUserInfoResponse struct {
Users []TailscaleUser `json:"users"`
}
// The scopes required for the tailscale provider
func TailscaleScopes() []string {
return []string{"users:read"}
}
// The tailscale endpoint
var TailscaleEndpoint = oauth2.Endpoint{
TokenURL: "https://api.tailscale.com/api/v2/oauth/token",
}
func GetTailscaleEmail(client *http.Client) (string, error) {
// Get the user info from tailscale using the oauth http client
res, err := client.Get("https://api.tailscale.com/api/v2/tailnet/-/users")
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Got response from tailscale")
// Read the body of the response
body, err := io.ReadAll(res.Body)
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Read body from tailscale")
// Parse the body into a user struct
var users TailscaleUserInfoResponse
// Unmarshal the body into the user struct
err = json.Unmarshal(body, &users)
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Parsed users from tailscale")
// Return the email of the first user
return users.Users[0].LoginName, nil
}

View File

@@ -22,11 +22,6 @@ type UnauthorizedQuery struct {
Resource string `url:"resource"` Resource string `url:"resource"`
} }
// TailscaleQuery is the query parameters for the tailscale endpoint
type TailscaleQuery struct {
Code int `url:"code"`
}
// Proxy is the uri parameters for the proxy endpoint // Proxy is the uri parameters for the proxy endpoint
type Proxy struct { type Proxy struct {
Proxy string `uri:"proxy" binding:"required"` Proxy string `uri:"proxy" binding:"required"`

View File

@@ -2,39 +2,36 @@ package types
// Config is the configuration for the tinyauth server // Config is the configuration for the tinyauth server
type Config struct { type Config struct {
Port int `mapstructure:"port" validate:"required"` Port int `mapstructure:"port" validate:"required"`
Address string `validate:"required,ip4_addr" mapstructure:"address"` Address string `validate:"required,ip4_addr" mapstructure:"address"`
Secret string `validate:"required,len=32" mapstructure:"secret"` Secret string `validate:"required,len=32" mapstructure:"secret"`
SecretFile string `mapstructure:"secret-file"` SecretFile string `mapstructure:"secret-file"`
AppURL string `validate:"required,url" mapstructure:"app-url"` AppURL string `validate:"required,url" mapstructure:"app-url"`
Users string `mapstructure:"users"` Users string `mapstructure:"users"`
UsersFile string `mapstructure:"users-file"` UsersFile string `mapstructure:"users-file"`
CookieSecure bool `mapstructure:"cookie-secure"` CookieSecure bool `mapstructure:"cookie-secure"`
GithubClientId string `mapstructure:"github-client-id"` GithubClientId string `mapstructure:"github-client-id"`
GithubClientSecret string `mapstructure:"github-client-secret"` GithubClientSecret string `mapstructure:"github-client-secret"`
GithubClientSecretFile string `mapstructure:"github-client-secret-file"` GithubClientSecretFile string `mapstructure:"github-client-secret-file"`
GoogleClientId string `mapstructure:"google-client-id"` GoogleClientId string `mapstructure:"google-client-id"`
GoogleClientSecret string `mapstructure:"google-client-secret"` GoogleClientSecret string `mapstructure:"google-client-secret"`
GoogleClientSecretFile string `mapstructure:"google-client-secret-file"` GoogleClientSecretFile string `mapstructure:"google-client-secret-file"`
TailscaleClientId string `mapstructure:"tailscale-client-id"` GenericClientId string `mapstructure:"generic-client-id"`
TailscaleClientSecret string `mapstructure:"tailscale-client-secret"` GenericClientSecret string `mapstructure:"generic-client-secret"`
TailscaleClientSecretFile string `mapstructure:"tailscale-client-secret-file"` GenericClientSecretFile string `mapstructure:"generic-client-secret-file"`
GenericClientId string `mapstructure:"generic-client-id"` GenericScopes string `mapstructure:"generic-scopes"`
GenericClientSecret string `mapstructure:"generic-client-secret"` GenericAuthURL string `mapstructure:"generic-auth-url"`
GenericClientSecretFile string `mapstructure:"generic-client-secret-file"` GenericTokenURL string `mapstructure:"generic-token-url"`
GenericScopes string `mapstructure:"generic-scopes"` GenericUserURL string `mapstructure:"generic-user-url"`
GenericAuthURL string `mapstructure:"generic-auth-url"` GenericName string `mapstructure:"generic-name"`
GenericTokenURL string `mapstructure:"generic-token-url"` DisableContinue bool `mapstructure:"disable-continue"`
GenericUserURL string `mapstructure:"generic-user-url"` OAuthWhitelist string `mapstructure:"oauth-whitelist"`
GenericName string `mapstructure:"generic-name"` SessionExpiry int `mapstructure:"session-expiry"`
DisableContinue bool `mapstructure:"disable-continue"` LogLevel int8 `mapstructure:"log-level" validate:"min=-1,max=5"`
OAuthWhitelist string `mapstructure:"oauth-whitelist"` Title string `mapstructure:"app-title"`
SessionExpiry int `mapstructure:"session-expiry"` EnvFile string `mapstructure:"env-file"`
LogLevel int8 `mapstructure:"log-level" validate:"min=-1,max=5"` LoginTimeout int `mapstructure:"login-timeout"`
Title string `mapstructure:"app-title"` LoginMaxRetries int `mapstructure:"login-max-retries"`
EnvFile string `mapstructure:"env-file"`
LoginTimeout int `mapstructure:"login-timeout"`
LoginMaxRetries int `mapstructure:"login-max-retries"`
} }
// Server configuration // Server configuration
@@ -47,19 +44,17 @@ type HandlersConfig struct {
// OAuthConfig is the configuration for the providers // OAuthConfig is the configuration for the providers
type OAuthConfig struct { type OAuthConfig struct {
GithubClientId string GithubClientId string
GithubClientSecret string GithubClientSecret string
GoogleClientId string GoogleClientId string
GoogleClientSecret string GoogleClientSecret string
TailscaleClientId string GenericClientId string
TailscaleClientSecret string GenericClientSecret string
GenericClientId string GenericScopes []string
GenericClientSecret string GenericAuthURL string
GenericScopes []string GenericTokenURL string
GenericAuthURL string GenericUserURL string
GenericTokenURL string AppURL string
GenericUserURL string
AppURL string
} }
// APIConfig is the configuration for the API // APIConfig is the configuration for the API

View File

@@ -213,7 +213,7 @@ func GetTinyauthLabels(labels map[string]string) types.TinyauthLabels {
// Check if any of the OAuth providers are configured based on the client id and secret // Check if any of the OAuth providers are configured based on the client id and secret
func OAuthConfigured(config types.Config) bool { func OAuthConfigured(config types.Config) bool {
return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "") || (config.TailscaleClientId != "" && config.TailscaleClientSecret != "") return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "")
} }
// Filter helper function // Filter helper function