Skip to content

Commit

Permalink
Optionally disable standard login
Browse files Browse the repository at this point in the history
  • Loading branch information
jordan-dalby committed Nov 25, 2024
1 parent ccfb5c6 commit 47e5e37
Show file tree
Hide file tree
Showing 7 changed files with 180 additions and 136 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ services:
TOKEN_EXPIRY: 24h
ALLOW_NEW_ACCOUNTS: "true"
DEBUG: "true"
DISABLE_ACCOUNTS: "false"
DISABLE_INTERNAL_ACCOUNTS: "false"
# See https://github.com/jordan-dalby/ByteStash/wiki/Single-Sign%E2%80%90on-Setup for more info
OIDC_ENABLED: "false"
Expand Down
124 changes: 65 additions & 59 deletions client/src/components/auth/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export const LoginPage: React.FC = () => {
window.location.href = `${window.__BASE_PATH__}/api/auth/oidc/auth`;
};

const showInternalRegistration = !authConfig?.disableInternalAccounts;

return (
<PageContainer className="flex items-center justify-center min-h-screen">
<div className="max-w-md w-full space-y-8">
Expand All @@ -75,7 +77,7 @@ export const LoginPage: React.FC = () => {
</h2>
<p className="mt-2 text-center text-sm text-gray-400">
Please sign in to continue
{authConfig?.allowNewAccounts ? (
{authConfig?.allowNewAccounts && showInternalRegistration ? (
<>
, create an{' '}
<Link to="/register" className="text-blue-400 hover:text-blue-300">
Expand All @@ -101,72 +103,76 @@ export const LoginPage: React.FC = () => {
>
Sign in with {oidcConfig.displayName}
</button>
<div className="relative my-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-700"></div>
</div>
<div className="relative flex justify-center">
<span className="px-2 bg-gray-900 text-gray-500 text-sm">
Or continue with password
</span>
{showInternalRegistration && (
<div className="relative my-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-700"></div>
</div>
<div className="relative flex justify-center">
<span className="px-2 bg-gray-900 text-gray-500 text-sm">
Or continue with password
</span>
</div>
</div>
</div>
)}
</>
)}

<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="rounded-md shadow-sm -space-y-px">
<div>
<input
type="text"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-t-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={isLoading}
/>
{showInternalRegistration && (
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="rounded-md shadow-sm -space-y-px">
<div>
<input
type="text"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-t-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={isLoading}
/>
</div>
<div>
<input
type="password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-b-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
/>
</div>
</div>

<div>
<input
type="password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-b-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
<button
type="submit"
className="group relative w-full flex justify-center py-2 px-4 border border-transparent
text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isLoading}
/>
>
{isLoading ? (
<span className="flex items-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Signing in...
</span>
) : (
'Sign in'
)}
</button>
</div>
</div>

<div>
<button
type="submit"
className="group relative w-full flex justify-center py-2 px-4 border border-transparent
text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isLoading}
>
{isLoading ? (
<span className="flex items-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Signing in...
</span>
) : (
'Sign in'
)}
</button>
</div>
</form>
</form>
)}
</div>
</PageContainer>
);
Expand Down
170 changes: 96 additions & 74 deletions client/src/components/auth/RegisterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export const RegisterPage: React.FC = () => {
window.location.href = `${window.__BASE_PATH__}/api/auth/oidc/auth`;
};

const showInternalRegistration = !authConfig?.disableInternalAccounts;

return (
<PageContainer className="flex items-center justify-center min-h-screen">
<div className="max-w-md w-full space-y-6">
Expand Down Expand Up @@ -137,91 +139,111 @@ export const RegisterPage: React.FC = () => {
>
Sign in with {oidcConfig.displayName}
</button>
<div className="relative my-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-700"></div>
</div>
<div className="relative flex justify-center">
<span className="px-2 bg-gray-900 text-gray-500 text-sm">
Or continue with password
</span>
{showInternalRegistration && (
<div className="relative my-6">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-700"></div>
</div>
<div className="relative flex justify-center">
<span className="px-2 bg-gray-900 text-gray-500 text-sm">
Or continue with password
</span>
</div>
</div>
</div>
)}
</>
)}

<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label htmlFor="username" className="sr-only">Username</label>
<input
id="username"
type="text"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-t-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={isLoading}
/>
</div>
<div>
<label htmlFor="password" className="sr-only">Password</label>
<input
id="password"
type="password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
/>
{showInternalRegistration && (
<form className="mt-8 space-y-6" onSubmit={handleSubmit}>
<div className="rounded-md shadow-sm -space-y-px">
<div>
<label htmlFor="username" className="sr-only">Username</label>
<input
id="username"
type="text"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-t-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Username"
value={username}
onChange={(e) => setUsername(e.target.value)}
disabled={isLoading}
/>
</div>
<div>
<label htmlFor="password" className="sr-only">Password</label>
<input
id="password"
type="password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
disabled={isLoading}
/>
</div>
<div>
<label htmlFor="confirm-password" className="sr-only">Confirm Password</label>
<input
id="confirm-password"
type="password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-b-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Confirm Password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
disabled={isLoading}
/>
</div>
</div>

<div>
<label htmlFor="confirm-password" className="sr-only">Confirm Password</label>
<input
id="confirm-password"
type="password"
required
className="appearance-none rounded-none relative block w-full px-3 py-2 border
border-gray-700 placeholder-gray-500 text-white bg-gray-800 rounded-b-md
focus:outline-none focus:ring-blue-500 focus:border-blue-500 focus:z-10 sm:text-sm"
placeholder="Confirm Password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
<button
type="submit"
className="group relative w-full flex justify-center py-2 px-4 border border-transparent
text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isLoading}
/>
>
{isLoading ? (
<span className="flex items-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Creating Account...
</span>
) : (
'Create Account'
)}
</button>
</div>
</div>
</form>
)}

<div>
<button
type="submit"
className="group relative w-full flex justify-center py-2 px-4 border border-transparent
text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed"
disabled={isLoading}
{!showInternalRegistration && !oidcConfig?.enabled && (
<div className="text-center">
<h2 className="text-xl font-bold text-red-400 mb-4">Registration Not Available</h2>
<p className="text-gray-400 mb-4">
Internal account registration is disabled and no SSO providers are configured.
Please contact your administrator.
</p>
<Link
to={ROUTES.PUBLIC_SNIPPETS}
className="text-blue-400 hover:text-blue-300"
>
{isLoading ? (
<span className="flex items-center">
<svg className="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Creating Account...
</span>
) : (
'Create Account'
)}
</button>
Browse public snippets
</Link>
</div>
</form>
)}
</div>
</PageContainer>
);
Expand Down
1 change: 1 addition & 0 deletions client/src/types/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export interface AuthConfig {
allowNewAccounts: boolean;
hasUsers: boolean;
disableAccounts: boolean;
disableInternalAccounts: boolean;
}
2 changes: 2 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ services:
- DEBUG=false
# Should we use accounts at all? When enabled, it will be like starting a fresh account so export your snippets, no login required
- DISABLE_ACCOUNTS=false
# Should internal accounts be disabled?
- DISABLE_INTERNAL_ACCOUNTS=false

# Optional: Enable OIDC for Single Sign On
- OIDC_ENABLED=true
Expand Down
2 changes: 2 additions & 0 deletions server/src/middleware/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const JWT_SECRET = getJwtSecret();
const ALLOW_NEW_ACCOUNTS = process.env.ALLOW_NEW_ACCOUNTS === 'true';
const TOKEN_EXPIRY = process.env.TOKEN_EXPIRY || '24h';
const DISABLE_ACCOUNTS = process.env.DISABLE_ACCOUNTS === 'true';
const DISABLE_INTERNAL_ACCOUNTS = process.env.DISABLE_INTERNAL_ACCOUNTS === 'true';

function generateAnonymousUsername() {
return `anon-${crypto.randomBytes(8).toString('hex')}`;
Expand Down Expand Up @@ -74,5 +75,6 @@ export {
TOKEN_EXPIRY,
ALLOW_NEW_ACCOUNTS,
DISABLE_ACCOUNTS,
DISABLE_INTERNAL_ACCOUNTS,
getOrCreateAnonymousUser
};
Loading

0 comments on commit 47e5e37

Please sign in to comment.