mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2025-10-28 12:45:47 +00:00
feat: style oauth auto redirect screen
This commit is contained in:
@@ -52,6 +52,7 @@ var rootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
|
rootCmd.FParseErrWhitelist.UnknownFlags = true
|
||||||
err := rootCmd.Execute()
|
err := rootCmd.Execute()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal().Err(err).Msg("Failed to execute command")
|
log.Fatal().Err(err).Msg("Failed to execute command")
|
||||||
@@ -92,7 +93,7 @@ func init() {
|
|||||||
{"ldap-search-filter", "(uid=%s)", "LDAP search filter for user lookup."},
|
{"ldap-search-filter", "(uid=%s)", "LDAP search filter for user lookup."},
|
||||||
{"resources-dir", "/data/resources", "Path to a directory containing custom resources (e.g. background image)."},
|
{"resources-dir", "/data/resources", "Path to a directory containing custom resources (e.g. background image)."},
|
||||||
{"database-path", "/data/tinyauth.db", "Path to the Sqlite database file."},
|
{"database-path", "/data/tinyauth.db", "Path to the Sqlite database file."},
|
||||||
{"trusted-proxies", "", "Comma separated list of trusted proxies (IP addresses) for correct client IP detection and for header ACLs."},
|
{"trusted-proxies", "", "Comma separated list of trusted proxies (IP addresses or CIDRs) for correct client IP detection."},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, opt := range configOptions {
|
for _, opt := range configOptions {
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
|
"loginOauthAutoRedirectTitle": "OAuth Auto Redirect",
|
||||||
|
"loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.",
|
||||||
|
"loginOauthAutoRedirectButton": "Redirect now",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueRedirectingTitle": "Redirecting...",
|
"continueRedirectingTitle": "Redirecting...",
|
||||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||||
|
|||||||
@@ -14,6 +14,9 @@
|
|||||||
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
"loginOauthFailSubtitle": "Failed to get OAuth URL",
|
||||||
"loginOauthSuccessTitle": "Redirecting",
|
"loginOauthSuccessTitle": "Redirecting",
|
||||||
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
|
||||||
|
"loginOauthAutoRedirectTitle": "OAuth Auto Redirect",
|
||||||
|
"loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.",
|
||||||
|
"loginOauthAutoRedirectButton": "Redirect now",
|
||||||
"continueTitle": "Continue",
|
"continueTitle": "Continue",
|
||||||
"continueRedirectingTitle": "Redirecting...",
|
"continueRedirectingTitle": "Redirecting...",
|
||||||
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
"continueRedirectingSubtitle": "You should be redirected to the app soon",
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export const ContinuePage = () => {
|
|||||||
const reveal = setTimeout(() => {
|
const reveal = setTimeout(() => {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
setShowRedirectButton(true);
|
setShowRedirectButton(true);
|
||||||
}, 1000);
|
}, 5000);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
clearTimeout(auto);
|
clearTimeout(auto);
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import { MicrosoftIcon } from "@/components/icons/microsoft";
|
|||||||
import { OAuthIcon } from "@/components/icons/oauth";
|
import { OAuthIcon } from "@/components/icons/oauth";
|
||||||
import { PocketIDIcon } from "@/components/icons/pocket-id";
|
import { PocketIDIcon } from "@/components/icons/pocket-id";
|
||||||
import { TailscaleIcon } from "@/components/icons/tailscale";
|
import { TailscaleIcon } from "@/components/icons/tailscale";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
CardDescription,
|
CardDescription,
|
||||||
CardContent,
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { OAuthButton } from "@/components/ui/oauth-button";
|
import { OAuthButton } from "@/components/ui/oauth-button";
|
||||||
import { SeperatorWithChildren } from "@/components/ui/separator";
|
import { SeperatorWithChildren } from "@/components/ui/separator";
|
||||||
@@ -20,7 +22,7 @@ import { useIsMounted } from "@/lib/hooks/use-is-mounted";
|
|||||||
import { LoginSchema } from "@/schemas/login-schema";
|
import { LoginSchema } from "@/schemas/login-schema";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import axios, { AxiosError } from "axios";
|
import axios, { AxiosError } from "axios";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Navigate, useLocation } from "react-router";
|
import { Navigate, useLocation } from "react-router";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
@@ -39,8 +41,12 @@ export const LoginPage = () => {
|
|||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const isMounted = useIsMounted();
|
const isMounted = useIsMounted();
|
||||||
|
const [oauthAutoRedirectHandover, setOauthAutoRedirectHandover] =
|
||||||
|
useState(false);
|
||||||
|
const [showRedirectButton, setShowRedirectButton] = useState(false);
|
||||||
|
|
||||||
const redirectTimer = useRef<number | null>(null);
|
const redirectTimer = useRef<number | null>(null);
|
||||||
|
const redirectButtonTimer = useRef<number | null>(null);
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(search);
|
const searchParams = new URLSearchParams(search);
|
||||||
const redirectUri = searchParams.get("redirect_uri");
|
const redirectUri = searchParams.get("redirect_uri");
|
||||||
@@ -67,6 +73,7 @@ export const LoginPage = () => {
|
|||||||
}, 500);
|
}, 500);
|
||||||
},
|
},
|
||||||
onError: () => {
|
onError: () => {
|
||||||
|
setOauthAutoRedirectHandover(false);
|
||||||
toast.error(t("loginOauthFailTitle"), {
|
toast.error(t("loginOauthFailTitle"), {
|
||||||
description: t("loginOauthFailSubtitle"),
|
description: t("loginOauthFailSubtitle"),
|
||||||
});
|
});
|
||||||
@@ -112,7 +119,11 @@ export const LoginPage = () => {
|
|||||||
!isLoggedIn &&
|
!isLoggedIn &&
|
||||||
redirectUri
|
redirectUri
|
||||||
) {
|
) {
|
||||||
|
setOauthAutoRedirectHandover(true);
|
||||||
oauthMutation.mutate(oauthAutoRedirect);
|
oauthMutation.mutate(oauthAutoRedirect);
|
||||||
|
redirectButtonTimer.current = window.setTimeout(() => {
|
||||||
|
setShowRedirectButton(true);
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
@@ -120,6 +131,8 @@ export const LoginPage = () => {
|
|||||||
useEffect(
|
useEffect(
|
||||||
() => () => {
|
() => () => {
|
||||||
if (redirectTimer.current) clearTimeout(redirectTimer.current);
|
if (redirectTimer.current) clearTimeout(redirectTimer.current);
|
||||||
|
if (redirectButtonTimer.current)
|
||||||
|
clearTimeout(redirectButtonTimer.current);
|
||||||
},
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
@@ -137,6 +150,31 @@ export const LoginPage = () => {
|
|||||||
return <Navigate to="/logout" replace />;
|
return <Navigate to="/logout" replace />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oauthAutoRedirectHandover) {
|
||||||
|
return (
|
||||||
|
<Card className="min-w-xs sm:min-w-sm">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-3xl">
|
||||||
|
{t("loginOauthAutoRedirectTitle")}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{t("loginOauthAutoRedirectSubtitle")}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
{showRedirectButton && (
|
||||||
|
<CardFooter className="flex flex-col items-stretch">
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
window.location.replace(oauthMutation.data?.data.url);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("loginOauthAutoRedirectButton")}
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Card className="min-w-xs sm:min-w-sm">
|
<Card className="min-w-xs sm:min-w-sm">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type Config struct {
|
|||||||
UsersFile string `mapstructure:"users-file"`
|
UsersFile string `mapstructure:"users-file"`
|
||||||
SecureCookie bool `mapstructure:"secure-cookie"`
|
SecureCookie bool `mapstructure:"secure-cookie"`
|
||||||
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"`
|
||||||
SessionExpiry int `mapstructure:"session-expiry"`
|
SessionExpiry int `mapstructure:"session-expiry"`
|
||||||
LogLevel string `mapstructure:"log-level" validate:"oneof=trace debug info warn error fatal panic"`
|
LogLevel string `mapstructure:"log-level" validate:"oneof=trace debug info warn error fatal panic"`
|
||||||
Title string `mapstructure:"app-title"`
|
Title string `mapstructure:"app-title"`
|
||||||
|
|||||||
Reference in New Issue
Block a user