forked from tsai/budibase
committed by
GitHub
18 changed files with 317 additions and 174 deletions
|
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,43 @@ |
|||||
|
<script> |
||||
|
import { Layout, Input } from "@budibase/bbui" |
||||
|
import { |
||||
|
createValidationStore, |
||||
|
requiredValidator, |
||||
|
} from "../../../helpers/validation" |
||||
|
|
||||
|
export let password |
||||
|
export let error |
||||
|
|
||||
|
const [firstPassword, passwordError, firstTouched] = createValidationStore( |
||||
|
"", |
||||
|
requiredValidator |
||||
|
) |
||||
|
const [repeatPassword, _, repeatTouched] = createValidationStore( |
||||
|
"", |
||||
|
requiredValidator |
||||
|
) |
||||
|
|
||||
|
$: password = $firstPassword |
||||
|
$: error = |
||||
|
!$firstPassword || |
||||
|
!$firstTouched || |
||||
|
!$repeatTouched || |
||||
|
$firstPassword !== $repeatPassword |
||||
|
</script> |
||||
|
|
||||
|
<Layout gap="XS" noPadding> |
||||
|
<Input |
||||
|
label="Password" |
||||
|
type="password" |
||||
|
error={$firstTouched && $passwordError} |
||||
|
bind:value={$firstPassword} |
||||
|
/> |
||||
|
<Input |
||||
|
label="Repeat Password" |
||||
|
type="password" |
||||
|
error={$repeatTouched && |
||||
|
$firstPassword !== $repeatPassword && |
||||
|
"Passwords must match"} |
||||
|
bind:value={$repeatPassword} |
||||
|
/> |
||||
|
</Layout> |
||||
@ -0,0 +1,60 @@ |
|||||
|
<script> |
||||
|
import { |
||||
|
Input, |
||||
|
Button, |
||||
|
Layout, |
||||
|
Body, |
||||
|
Heading, |
||||
|
notifications, |
||||
|
} from "@budibase/bbui" |
||||
|
import { auth } from "stores/backend" |
||||
|
|
||||
|
let email = "" |
||||
|
|
||||
|
async function forgot() { |
||||
|
try { |
||||
|
await auth.forgotPassword(email) |
||||
|
notifications.success("Email sent - please check your inbox") |
||||
|
} catch (err) { |
||||
|
notifications.error("Unable to send reset password link") |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="login"> |
||||
|
<div class="main"> |
||||
|
<Layout> |
||||
|
<Layout noPadding justifyItems="center"> |
||||
|
<img src="https://i.imgur.com/ZKyklgF.png" /> |
||||
|
</Layout> |
||||
|
<Layout gap="XS" noPadding> |
||||
|
<Heading textAlign="center">Forgotten your password?</Heading> |
||||
|
<Body size="S" textAlign="center"> |
||||
|
No problem! Just enter your account's email address and we'll send |
||||
|
you a link to reset it. |
||||
|
</Body> |
||||
|
<Input label="Email" bind:value={email} /> |
||||
|
</Layout> |
||||
|
<Button cta on:click={forgot} disabled={!email}>Reset your password</Button> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.login { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.main { |
||||
|
width: 300px; |
||||
|
} |
||||
|
|
||||
|
img { |
||||
|
width: 48px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,43 @@ |
|||||
|
<script> |
||||
|
import { Link } from "@budibase/bbui" |
||||
|
import GoogleLogo from "/assets/google-logo.png" |
||||
|
</script> |
||||
|
|
||||
|
<div class="outer"> |
||||
|
<Link target="_blank" href="/api/admin/auth/google"> |
||||
|
<div class="inner"> |
||||
|
<img src={GoogleLogo} alt="google icon" /> |
||||
|
<p>Sign in with Google</p> |
||||
|
</div> |
||||
|
</Link> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.outer { |
||||
|
border: 1px solid #494949; |
||||
|
border-radius: 4px; |
||||
|
width: 100%; |
||||
|
background-color: var(--background-alt); |
||||
|
} |
||||
|
.inner { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding-top: var(--spacing-xs); |
||||
|
padding-bottom: var(--spacing-xs); |
||||
|
} |
||||
|
.inner img { |
||||
|
width: 18px; |
||||
|
margin: 3px 10px 3px 3px; |
||||
|
} |
||||
|
.inner p { |
||||
|
margin: 0; |
||||
|
} |
||||
|
.outer :global(a) { |
||||
|
text-decoration: none; |
||||
|
font-weight: 500; |
||||
|
font-size: var(--font-size-m); |
||||
|
color: #fff; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,58 @@ |
|||||
|
<script> |
||||
|
import { Button, Layout, Body, Heading, notifications } from "@budibase/bbui" |
||||
|
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte" |
||||
|
import { params, goto } from "@roxi/routify" |
||||
|
import { auth } from "stores/backend" |
||||
|
|
||||
|
const resetCode = $params["?code"] |
||||
|
let password, error |
||||
|
|
||||
|
async function reset() { |
||||
|
try { |
||||
|
await auth.resetPassword(password, resetCode) |
||||
|
notifications.success("Password reset successfully") |
||||
|
// send them to login if reset successful |
||||
|
$goto("./login") |
||||
|
} catch (err) { |
||||
|
notifications.error("Unable to reset password") |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div class="login"> |
||||
|
<div class="main"> |
||||
|
<Layout> |
||||
|
<Layout noPadding justifyItems="center"> |
||||
|
<img src="https://i.imgur.com/ZKyklgF.png" /> |
||||
|
</Layout> |
||||
|
<Layout gap="XS" noPadding> |
||||
|
<Heading textAlign="center">Reset your password</Heading> |
||||
|
<Body size="S" textAlign="center"> |
||||
|
Please enter the new password you'd like to use. |
||||
|
</Body> |
||||
|
<PasswordRepeatInput bind:password bind:error /> |
||||
|
</Layout> |
||||
|
<Button cta on:click={reset} disabled={error || !resetCode}>Reset your password</Button> |
||||
|
</Layout> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<style> |
||||
|
.login { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.main { |
||||
|
width: 260px; |
||||
|
} |
||||
|
|
||||
|
img { |
||||
|
width: 48px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<script> |
||||
|
import ForgotForm from "components/login/ForgotForm.svelte" |
||||
|
</script> |
||||
|
|
||||
|
<ForgotForm /> |
||||
@ -0,0 +1,5 @@ |
|||||
|
<script> |
||||
|
import ResetForm from "components/login/ResetForm.svelte" |
||||
|
</script> |
||||
|
|
||||
|
<ResetForm /> |
||||
@ -1,93 +0,0 @@ |
|||||
const authPkg = require("@budibase/auth") |
|
||||
const { google } = require("@budibase/auth/src/middleware") |
|
||||
const { Configs } = require("../../constants") |
|
||||
const CouchDB = require("../../db") |
|
||||
const { clearCookie } = authPkg.utils |
|
||||
const { Cookies } = authPkg.constants |
|
||||
const { passport } = authPkg.auth |
|
||||
|
|
||||
const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name |
|
||||
|
|
||||
exports.authenticate = async (ctx, next) => { |
|
||||
return passport.authenticate("local", async (err, user) => { |
|
||||
if (err) { |
|
||||
return ctx.throw(403, "Unauthorized") |
|
||||
} |
|
||||
|
|
||||
const expires = new Date() |
|
||||
expires.setDate(expires.getDate() + 1) |
|
||||
|
|
||||
if (!user) { |
|
||||
return ctx.throw(403, "Unauthorized") |
|
||||
} |
|
||||
|
|
||||
ctx.cookies.set(Cookies.Auth, user.token, { |
|
||||
expires, |
|
||||
path: "/", |
|
||||
httpOnly: false, |
|
||||
overwrite: true, |
|
||||
}) |
|
||||
|
|
||||
delete user.token |
|
||||
|
|
||||
ctx.body = { user } |
|
||||
})(ctx, next) |
|
||||
} |
|
||||
|
|
||||
exports.logout = async ctx => { |
|
||||
clearCookie(ctx, Cookies.Auth) |
|
||||
ctx.body = { message: "User logged out" } |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* The initial call that google authentication makes to take you to the google login screen. |
|
||||
* On a successful login, you will be redirected to the googleAuth callback route. |
|
||||
*/ |
|
||||
exports.googlePreAuth = async (ctx, next) => { |
|
||||
const db = new CouchDB(GLOBAL_DB) |
|
||||
const config = await authPkg.db.getScopedFullConfig(db, { |
|
||||
type: Configs.GOOGLE, |
|
||||
group: ctx.query.group, |
|
||||
}) |
|
||||
const strategy = await google.strategyFactory(config) |
|
||||
|
|
||||
return passport.authenticate(strategy, { |
|
||||
scope: ["profile", "email"], |
|
||||
})(ctx, next) |
|
||||
} |
|
||||
|
|
||||
exports.googleAuth = async (ctx, next) => { |
|
||||
const db = new CouchDB(GLOBAL_DB) |
|
||||
|
|
||||
const config = await authPkg.db.getScopedFullConfig(db, { |
|
||||
type: Configs.GOOGLE, |
|
||||
group: ctx.query.group, |
|
||||
}) |
|
||||
const strategy = await google.strategyFactory(config) |
|
||||
|
|
||||
return passport.authenticate( |
|
||||
strategy, |
|
||||
{ successRedirect: "/", failureRedirect: "/error" }, |
|
||||
async (err, user) => { |
|
||||
if (err) { |
|
||||
return ctx.throw(403, "Unauthorized") |
|
||||
} |
|
||||
|
|
||||
const expires = new Date() |
|
||||
expires.setDate(expires.getDate() + 1) |
|
||||
|
|
||||
if (!user) { |
|
||||
return ctx.throw(403, "Unauthorized") |
|
||||
} |
|
||||
|
|
||||
ctx.cookies.set(Cookies.Auth, user.token, { |
|
||||
expires, |
|
||||
path: "/", |
|
||||
httpOnly: false, |
|
||||
overwrite: true, |
|
||||
}) |
|
||||
|
|
||||
ctx.redirect("/") |
|
||||
} |
|
||||
)(ctx, next) |
|
||||
} |
|
||||
Loading…
Reference in new issue