Building up the API interactions for the forgotten password flow.
This commit is contained in:
parent
46d572a8fc
commit
d9439abe86
|
@ -1,10 +1,24 @@
|
||||||
<script>
|
<script>
|
||||||
import { Input, Button, Layout, Body, Heading } from "@budibase/bbui"
|
import {
|
||||||
|
Input,
|
||||||
|
Button,
|
||||||
|
Layout,
|
||||||
|
Body,
|
||||||
|
Heading,
|
||||||
|
notifications,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
import { auth } from "stores/backend"
|
||||||
|
|
||||||
let username = ""
|
let email = ""
|
||||||
let password = ""
|
|
||||||
|
|
||||||
async function reset() {}
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="login">
|
<div class="login">
|
||||||
|
@ -15,13 +29,13 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading textAlign="center">Forgotten your password?</Heading>
|
<Heading textAlign="center">Forgotten your password?</Heading>
|
||||||
<Body size="S" textAlign="center"
|
<Body size="S" textAlign="center">
|
||||||
>No problem! Just enter your account's email address and we'll send
|
No problem! Just enter your account's email address and we'll send
|
||||||
you a link to reset it.</Body
|
you a link to reset it.
|
||||||
>
|
</Body>
|
||||||
<Input label="Email" bind:value={username} />
|
<Input label="Email" bind:value={email} />
|
||||||
</Layout>
|
</Layout>
|
||||||
<Button cta on:click={reset}>Reset your password</Button>
|
<Button cta on:click={forgot} disabled={!email}>Reset your password</Button>
|
||||||
</Layout>
|
</Layout>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -52,9 +52,9 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout gap="S" noPadding>
|
<Layout gap="S" noPadding>
|
||||||
<Button cta on:click={login}>Sign in to Budibase</Button>
|
<Button cta on:click={login}>Sign in to Budibase</Button>
|
||||||
<ActionButton quiet on:click={() => $goto("./forgot")}
|
<ActionButton quiet on:click={() => $goto("./forgot")}>
|
||||||
>Forgot password?</ActionButton
|
Forgot password?
|
||||||
>
|
</ActionButton>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
<script>
|
<script>
|
||||||
import { Input, Button, Layout, Body, Heading } from "@budibase/bbui"
|
import { Button, Layout, Body, Heading, notifications } from "@budibase/bbui"
|
||||||
import { params } from "@roxi/routify"
|
import PasswordRepeatInput from "components/common/users/PasswordRepeatInput.svelte"
|
||||||
|
import { params, goto } from "@roxi/routify"
|
||||||
import { auth } from "stores/backend"
|
import { auth } from "stores/backend"
|
||||||
|
|
||||||
const resetCode = $params["?code"]
|
const resetCode = $params["?code"]
|
||||||
let password = ""
|
let password, error
|
||||||
|
|
||||||
async function reset() {}
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="login">
|
<div class="login">
|
||||||
|
@ -17,12 +28,12 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading textAlign="center">Reset your password</Heading>
|
<Heading textAlign="center">Reset your password</Heading>
|
||||||
<Body size="S" textAlign="center"
|
<Body size="S" textAlign="center">
|
||||||
>Please enter the new password you'd like to use.</Body
|
Please enter the new password you'd like to use.
|
||||||
>
|
</Body>
|
||||||
<Input label="Password" bind:value={password} />
|
<PasswordRepeatInput bind:password bind:error />
|
||||||
</Layout>
|
</Layout>
|
||||||
<Button cta on:click={reset}>Reset your password</Button>
|
<Button cta on:click={reset} disabled={error || !resetCode}>Reset your password</Button>
|
||||||
</Layout>
|
</Layout>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -33,6 +33,25 @@ export function createAuthStore() {
|
||||||
await response.json()
|
await response.json()
|
||||||
store.update(state => ({ ...state, user: null }))
|
store.update(state => ({ ...state, user: null }))
|
||||||
},
|
},
|
||||||
|
forgotPassword: async email => {
|
||||||
|
const response = await api.post(`/api/admin/auth/reset`, {
|
||||||
|
email,
|
||||||
|
})
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw "Unable to send email with reset link"
|
||||||
|
}
|
||||||
|
await response.json()
|
||||||
|
},
|
||||||
|
resetPassword: async (password, code) => {
|
||||||
|
const response = await api.post(`/api/admin/auth/reset/update`, {
|
||||||
|
password,
|
||||||
|
resetCode: code,
|
||||||
|
})
|
||||||
|
if (response.status !== 200) {
|
||||||
|
throw "Unable to reset password"
|
||||||
|
}
|
||||||
|
await response.json()
|
||||||
|
},
|
||||||
createUser: async user => {
|
createUser: async user => {
|
||||||
const response = await api.post(`/api/admin/users`, user)
|
const response = await api.post(`/api/admin/users`, user)
|
||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
|
|
|
@ -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