Compare commits

..

29 Commits

Author SHA1 Message Date
Stavros
97b0d3e350 fix: use axios error instead of generic error in login page 2025-05-20 17:16:08 +03:00
Stavros
e8190456c3 feat: validate api response against zod schema 2025-05-20 17:12:39 +03:00
Stavros
02f83baa4d chore: formatting 2025-05-20 17:02:10 +03:00
Stavros
d59f35467e Merge branch 'main' into feat/new-ui 2025-05-20 16:49:19 +03:00
Stavros
00fbbfc4f2 chore: remove translations cdn 2025-05-15 16:07:38 +03:00
Stavros
76346fe835 fix: fix hook rendering 2025-05-15 16:05:21 +03:00
Stavros
03f193090d fix: fix dockerfiles 2025-05-15 15:57:12 +03:00
Stavros
3612ac0740 refactor: change select color 2025-05-15 15:50:34 +03:00
Stavros
00203722f8 fix: run oauth auto redirect only when there is a redirect URI 2025-05-14 21:00:06 +03:00
Stavros
60093997dc fix: correctly redirect to app and check for untrusted redirects 2025-05-14 20:56:24 +03:00
Stavros
ada21776bc refactor: bot suggestions 2025-05-14 20:43:18 +03:00
Stavros
b12d0655d4 chore: fix dependabot config 2025-05-14 20:26:38 +03:00
Stavros
2246ca0c13 Merge branch 'main' into feat/new-ui 2025-05-14 20:21:37 +03:00
Stavros
10dc228f6c feat: sanitize redirect URL on check 2025-05-14 20:08:48 +03:00
Stavros
28eea8d40c feat: sanitize redirect URL 2025-05-14 20:08:04 +03:00
Stavros
a4224e6771 chore: add acknowledgements for background image 2025-05-14 19:50:44 +03:00
Stavros
003f55b9ff feat: custom background image config option 2025-05-14 19:47:56 +03:00
Stavros
dbc460144e feat: favicons 2025-05-14 19:36:57 +03:00
Stavros
f05181b05d chore: rename docs back to assets 2025-05-14 19:29:28 +03:00
Stavros
28ef893456 chore: update readme and assets 2025-05-12 23:16:17 +03:00
Stavros
a488b70bbe feat: add oauth logic 2025-05-12 23:03:14 +03:00
Stavros
4e91e567b2 fix: use correct tab order in login form 2025-05-09 23:17:53 +03:00
Stavros
6453edede6 feat: finalize username login 2025-05-09 22:55:52 +03:00
Stavros
41c63e5b49 feat: user context 2025-05-09 17:39:14 +03:00
Stavros
31a7b0ff06 feat: app context 2025-05-09 16:49:54 +03:00
Stavros
0880152b48 chore: remove unused translations 2025-05-09 16:26:26 +03:00
Stavros
51532350cc feat: finalize pages 2025-05-09 16:25:12 +03:00
Stavros
56ae246ff4 feat: make forms functional 2025-05-08 18:08:56 +03:00
Stavros
fd96f39d3a wip 2025-04-29 22:36:48 +03:00
8 changed files with 20 additions and 47 deletions

View File

@@ -30,4 +30,3 @@ APP_TITLE=Tinyauth SSO
FORGOT_PASSWORD_MESSAGE=Some message about resetting the password FORGOT_PASSWORD_MESSAGE=Some message about resetting the password
OAUTH_AUTO_REDIRECT=none OAUTH_AUTO_REDIRECT=none
BACKGROUND_IMAGE=some_image_url BACKGROUND_IMAGE=some_image_url
GENERIC_SKIP_SSL=false

View File

@@ -52,4 +52,7 @@ COPY --from=builder /tinyauth/tinyauth ./
EXPOSE 3000 EXPOSE 3000
HEALTHCHECK --interval=10s --timeout=5s \
CMD curl -f http://localhost:3000/api/healthcheck || exit 1
ENTRYPOINT ["./tinyauth"] ENTRYPOINT ["./tinyauth"]

View File

@@ -24,22 +24,18 @@ Tinyauth is a simple authentication middleware that adds simple username/passwor
> [!NOTE] > [!NOTE]
> Tinyauth is intended for homelab use only and it is not made for production use cases. If you are looking for something production ready please use [authentik](https://goauthentik.io) instead. > Tinyauth is intended for homelab use only and it is not made for production use cases. If you are looking for something production ready please use [authentik](https://goauthentik.io) instead.
## Discord
I just made a Discord server for tinyauth! It is not only for tinyauth but general self-hosting and homelabbing. [See you there!](https://discord.gg/eHzVaCzRRd).
## Getting Started ## Getting Started
You can easily get started with tinyauth by following the guide in the [documentation](https://tinyauth.app/docs/getting-started.html). There is also an available [docker compose file](./docker-compose.example.yml) that has traefik, whoami and tinyauth to demonstrate its capabilities. You can easily get started with tinyauth by following the guide in the [documentation](https://tinyauth.app/docs/getting-started.html). There is also an available [docker compose file](./docker-compose.example.yml) that has traefik, whoami and tinyauth to demonstrate its capabilities.
## Demo
If you are still not sure if tinyauth suits your needs you can try out the [demo](https://demo.tinyauth.app). The default username is `user` and the default password is `password`.
## Documentation ## Documentation
You can find documentation and guides on all of the available configuration of tinyauth in the [website](https://tinyauth.app). You can find documentation and guides on all of the available configuration of tinyauth in the [website](https://tinyauth.app).
## Discord
I just made a Discord server for tinyauth! It is not only for tinyauth but general self-hosting and homelabbing. [See you there!](https://discord.gg/eHzVaCzRRd).
## Contributing ## Contributing
All contributions to the codebase are welcome! If you have any recommendations on how to improve security or find a security issue in tinyauth please open an issue or pull request so it can be fixed as soon as possible! All contributions to the codebase are welcome! If you have any recommendations on how to improve security or find a security issue in tinyauth please open an issue or pull request so it can be fixed as soon as possible!

View File

@@ -79,7 +79,6 @@ var rootCmd = &cobra.Command{
GenericAuthURL: config.GenericAuthURL, GenericAuthURL: config.GenericAuthURL,
GenericTokenURL: config.GenericTokenURL, GenericTokenURL: config.GenericTokenURL,
GenericUserURL: config.GenericUserURL, GenericUserURL: config.GenericUserURL,
GenericSkipSSL: config.GenericSkipSSL,
AppURL: config.AppURL, AppURL: config.AppURL,
} }
@@ -198,7 +197,6 @@ func init() {
rootCmd.Flags().String("generic-token-url", "", "Generic OAuth token URL.") rootCmd.Flags().String("generic-token-url", "", "Generic OAuth token URL.")
rootCmd.Flags().String("generic-user-url", "", "Generic OAuth user info URL.") rootCmd.Flags().String("generic-user-url", "", "Generic OAuth user info URL.")
rootCmd.Flags().String("generic-name", "Generic", "Generic OAuth provider name.") rootCmd.Flags().String("generic-name", "Generic", "Generic OAuth provider name.")
rootCmd.Flags().Bool("generic-skip-ssl", false, "Skip SSL verification for the generic OAuth provider.")
rootCmd.Flags().Bool("disable-continue", false, "Disable continue screen and redirect to app directly.") rootCmd.Flags().Bool("disable-continue", false, "Disable continue screen and redirect to app directly.")
rootCmd.Flags().String("oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth.") rootCmd.Flags().String("oauth-whitelist", "", "Comma separated list of email addresses to whitelist when using OAuth.")
rootCmd.Flags().String("oauth-auto-redirect", "none", "Auto redirect to the specified OAuth provider if configured. (available providers: github, google, generic)") rootCmd.Flags().String("oauth-auto-redirect", "none", "Auto redirect to the specified OAuth provider if configured. (available providers: github, google, generic)")
@@ -233,7 +231,6 @@ func init() {
viper.BindEnv("generic-token-url", "GENERIC_TOKEN_URL") viper.BindEnv("generic-token-url", "GENERIC_TOKEN_URL")
viper.BindEnv("generic-user-url", "GENERIC_USER_URL") viper.BindEnv("generic-user-url", "GENERIC_USER_URL")
viper.BindEnv("generic-name", "GENERIC_NAME") viper.BindEnv("generic-name", "GENERIC_NAME")
viper.BindEnv("generic-skip-ssl", "GENERIC_SKIP_SSL")
viper.BindEnv("disable-continue", "DISABLE_CONTINUE") viper.BindEnv("disable-continue", "DISABLE_CONTINUE")
viper.BindEnv("oauth-whitelist", "OAUTH_WHITELIST") viper.BindEnv("oauth-whitelist", "OAUTH_WHITELIST")
viper.BindEnv("oauth-auto-redirect", "OAUTH_AUTO_REDIRECT") viper.BindEnv("oauth-auto-redirect", "OAUTH_AUTO_REDIRECT")

View File

@@ -29,7 +29,7 @@ export const LoginPage = () => {
return <Navigate to="/logout" />; return <Navigate to="/logout" />;
} }
const { configuredProviders, title, oauthAutoRedirect, genericName } = useAppContext(); const { configuredProviders, title, oauthAutoRedirect } = useAppContext();
const { search } = useLocation(); const { search } = useLocation();
const { t } = useTranslation(); const { t } = useTranslation();
const isMounted = useIsMounted(); const isMounted = useIsMounted();
@@ -138,7 +138,7 @@ export const LoginPage = () => {
)} )}
{configuredProviders.includes("generic") && ( {configuredProviders.includes("generic") && (
<OAuthButton <OAuthButton
title={genericName} title="Generic"
icon={<GenericIcon />} icon={<GenericIcon />}
className="w-full" className="w-full"
onClick={() => oauthMutation.mutate("generic")} onClick={() => oauthMutation.mutate("generic")}

View File

@@ -3,48 +3,28 @@ package oauth
import ( import (
"context" "context"
"crypto/rand" "crypto/rand"
"crypto/tls"
"encoding/base64" "encoding/base64"
"net/http" "net/http"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
func NewOAuth(config oauth2.Config, insecureSkipVerify bool) *OAuth { func NewOAuth(config oauth2.Config) *OAuth {
return &OAuth{ return &OAuth{
Config: config, Config: config,
InsecureSkipVerify: insecureSkipVerify,
} }
} }
type OAuth struct { type OAuth struct {
Config oauth2.Config Config oauth2.Config
Context context.Context Context context.Context
Token *oauth2.Token Token *oauth2.Token
Verifier string Verifier string
InsecureSkipVerify bool
} }
func (oauth *OAuth) Init() { func (oauth *OAuth) Init() {
// Create transport with TLS // Create a new context and verifier
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: oauth.InsecureSkipVerify,
MinVersion: tls.VersionTLS12,
},
}
// Create a new context
oauth.Context = context.Background() oauth.Context = context.Background()
// Create the HTTP client with the transport
httpClient := &http.Client{
Transport: transport,
}
// Set the HTTP client in the context
oauth.Context = context.WithValue(oauth.Context, oauth2.HTTPClient, httpClient)
// Create the verifier
oauth.Verifier = oauth2.GenerateVerifier() oauth.Verifier = oauth2.GenerateVerifier()
} }

View File

@@ -36,7 +36,7 @@ func (providers *Providers) Init() {
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/github", providers.Config.AppURL), RedirectURL: fmt.Sprintf("%s/api/oauth/callback/github", providers.Config.AppURL),
Scopes: GithubScopes(), Scopes: GithubScopes(),
Endpoint: endpoints.GitHub, Endpoint: endpoints.GitHub,
}, false) })
// Initialize the oauth provider // Initialize the oauth provider
providers.Github.Init() providers.Github.Init()
@@ -53,7 +53,7 @@ func (providers *Providers) Init() {
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/google", providers.Config.AppURL), RedirectURL: fmt.Sprintf("%s/api/oauth/callback/google", providers.Config.AppURL),
Scopes: GoogleScopes(), Scopes: GoogleScopes(),
Endpoint: endpoints.Google, Endpoint: endpoints.Google,
}, false) })
// Initialize the oauth provider // Initialize the oauth provider
providers.Google.Init() providers.Google.Init()
@@ -73,7 +73,7 @@ func (providers *Providers) Init() {
AuthURL: providers.Config.GenericAuthURL, AuthURL: providers.Config.GenericAuthURL,
TokenURL: providers.Config.GenericTokenURL, TokenURL: providers.Config.GenericTokenURL,
}, },
}, providers.Config.GenericSkipSSL) })
// Initialize the oauth provider // Initialize the oauth provider
providers.Generic.Init() providers.Generic.Init()

View File

@@ -24,7 +24,6 @@ type Config struct {
GenericTokenURL string `mapstructure:"generic-token-url"` GenericTokenURL string `mapstructure:"generic-token-url"`
GenericUserURL string `mapstructure:"generic-user-url"` GenericUserURL string `mapstructure:"generic-user-url"`
GenericName string `mapstructure:"generic-name"` GenericName string `mapstructure:"generic-name"`
GenericSkipSSL bool `mapstructure:"generic-skip-ssl"`
DisableContinue bool `mapstructure:"disable-continue"` DisableContinue bool `mapstructure:"disable-continue"`
OAuthWhitelist string `mapstructure:"oauth-whitelist"` OAuthWhitelist string `mapstructure:"oauth-whitelist"`
OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"` OAuthAutoRedirect string `mapstructure:"oauth-auto-redirect" validate:"oneof=none github google generic"`
@@ -35,7 +34,7 @@ type Config struct {
LoginTimeout int `mapstructure:"login-timeout"` LoginTimeout int `mapstructure:"login-timeout"`
LoginMaxRetries int `mapstructure:"login-max-retries"` LoginMaxRetries int `mapstructure:"login-max-retries"`
FogotPasswordMessage string `mapstructure:"forgot-password-message" validate:"required"` FogotPasswordMessage string `mapstructure:"forgot-password-message" validate:"required"`
BackgroundImage string `mapstructure:"background-image" validate:"required"` BackgroundImage string `mapstructure:"background-image" validate:"required,url"`
} }
// Server configuration // Server configuration
@@ -63,7 +62,6 @@ type OAuthConfig struct {
GenericAuthURL string GenericAuthURL string
GenericTokenURL string GenericTokenURL string
GenericUserURL string GenericUserURL string
GenericSkipSSL bool
AppURL string AppURL string
} }