From d0dbacfc4bfb080793340a847e876ca46df55dfa Mon Sep 17 00:00:00 2001 From: Iain Collins Date: Mon, 27 Jul 2020 03:11:43 +0100 Subject: [PATCH] Display some error messages on the sign in page Improves the UX by displaying some error messages on the sign in page --- package.json | 2 +- src/css/index.css | 38 ++++++++++++++++++++++-------- src/server/index.js | 8 +++++-- src/server/pages/error.js | 48 ++++---------------------------------- src/server/pages/index.js | 1 + src/server/pages/signin.js | 31 +++++++++++++++++++++++- www/src/css/buttons.css | 4 ++-- 7 files changed, 73 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index e1d5aaa5..a6bf2fc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "next-auth", - "version": "3.0.0-beta.24", + "version": "3.0.0-beta.25", "description": "Authentication for Next.js", "homepage": "https://next-auth.js.org", "repository": "https://github.com/iaincollins/next-auth.git", diff --git a/src/css/index.css b/src/css/index.css index da3d7c42..12e3a306 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -1,14 +1,18 @@ :root { + --color-background: #fff; --color-primary: #444; --color-control-border: #bbb; - --color-button-hover-background: #f9f9f9; - --color-button-active-background: #f5f5f5; + --color-button-active-background: #f9f9f9; --color-button-active-border: #aaa; --border-width: 1px; --border-radius: .3rem; + --color-error: #c94b4b; + --color-info: #157efb; + --color-seperator: #ccc; } body { + background-color: var(--color-background); margin: 0; padding: 0; font-family: -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; @@ -39,7 +43,7 @@ input[type] { width: 100%; padding: .5rem 1rem; border: var(--border-width) solid var(--color-control-border); - background: #fff; + background: var(--color-background); font-size: 1rem; border-radius: var(--border-radius); box-shadow: inset 0 .1rem .2rem rgba(0,0,0,.2); @@ -61,7 +65,7 @@ a.button { line-height: 1rem; &:link, &:visited { - background-color: #fff; + background-color: var(--color-background); color: var(--color-primary); } } @@ -72,21 +76,20 @@ a.button { padding: .75rem 1rem; border: var(--border-width) solid var(--color-control-border); color: var(--color-primary); - background-color: #fff; + background-color: var(--color-background); font-size: 1rem; border-radius: var(--border-radius); transition: all .1s ease-in-out; - box-shadow: 0 0.15rem 0.3rem rgba(0,0,0,.15), inset 0 .1rem .2rem #fff, inset 0 -.1rem .1rem rgba(0,0,0,.05); + box-shadow: 0 0.15rem 0.3rem rgba(0,0,0,.15), inset 0 .1rem .2rem var(--color-background), inset 0 -.1rem .1rem rgba(0,0,0,.05); font-weight: 500; position: relative; &:hover { - background-color: var(--color-button-hover-background); cursor: pointer; } &:active { - box-shadow: 0 0.15rem 0.3rem rgba(0,0,0,.15), inset 0 .1rem .2rem #fff, inset 0 -.1rem .1rem rgba(0,0,0,.1); + box-shadow: 0 0.15rem 0.3rem rgba(0,0,0,.15), inset 0 .1rem .2rem var(--color-background), inset 0 -.1rem .1rem rgba(0,0,0,.1); background-color: var(--color-button-active-background); border-color: var(--color-button-active-border); cursor: pointer; @@ -143,19 +146,34 @@ a.site { hr { display: block; border: 0; - border-top: 1px solid #ccc; + border-top: 1px solid var(--color-seperator); margin: 1.5em auto 0 auto; overflow: visible; &::before { content: "or"; - background: #fff; + background: var(--color-background); color: #888; padding: 0 .4rem; position: relative; top: -.6rem; } } + + .error { + background: #f5f5f5; + font-weight: 500; + border-radius: 0.3rem; + background: var(--color-info); + color: #fff; + p { + text-align: left; + padding: 0.5rem 1rem; + font-size: 0.9rem; + line-height: 1.2rem; + } + } + > div, form { display: block; diff --git a/src/server/index.js b/src/server/index.js index 45b65538..6c8615f7 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -235,12 +235,16 @@ export default async (req, res, userSuppliedOptions) => { res.json({ csrfToken }) return done() case 'signin': - if (options.pages.signIn) { return redirect(`${options.pages.signIn}${options.pages.signIn.includes('?') ? '&' : '?'}callbackUrl=${options.callbackUrl}`) } + if (options.pages.signIn) { + let redirectUrl = `${options.pages.signIn}${options.pages.signIn.includes('?') ? '&' : '?'}callbackUrl=${options.callbackUrl}` + if (req.query.error) { redirectUrl = `${redirectUrl}&error=${req.query.error}` } + return redirect(redirectUrl) + } pages.render(req, res, 'signin', { baseUrl, basePath, providers: Object.values(options.providers), callbackUrl: options.callbackUrl, csrfToken }, done) break case 'signout': - if (options.pages.signOut) { return redirect(`${options.pages.signOut}${options.pages.signOut.includes('?') ? '&' : '?'}callbackUrl=${options.callbackUrl}`) } + if (options.pages.signOut) { return redirect(`${options.pages.signOut}${options.pages.signOut.includes('?') ? '&' : '?'}error=${error}`) } pages.render(req, res, 'signout', { baseUrl, basePath, csrfToken, callbackUrl: options.callbackUrl }, done) break diff --git a/src/server/pages/error.js b/src/server/pages/error.js index ad263c16..36ab15ff 100644 --- a/src/server/pages/error.js +++ b/src/server/pages/error.js @@ -2,7 +2,7 @@ import { h } from 'preact' // eslint-disable-line no-unused-vars import render from 'preact-render-to-string' export default ({ baseUrl, basePath, error, res }) => { - const signinPageUrl = `${baseUrl}${basePath}/signin` // @TODO Make sign in URL configurable + const signinPageUrl = `${baseUrl}${basePath}/signin` let statusCode = 200 let heading =

Error

@@ -15,51 +15,13 @@ export default ({ baseUrl, basePath, error, res }) => { case 'OAuthCreateAccount': case 'EmailCreateAccount': case 'Callback': - heading =

Sign in failed

- message = -
-
-

Try signing with a different account.

-
-

Sign in

-
- break case 'OAuthAccountNotLinked': - statusCode = 403 - heading =

Sign in failed

- message = -
-
-

An account associated with your email address already exists.

-

Sign in with the same account you used originally to confirm your identity.

-
-

Sign in

-
- // @TODO Add this text when account linking is complete - //

Once you are signed in, you can link your accounts.

- // @TODO Display email sign in option if an email provider is configured - break case 'EmailSignin': - heading =

Sign in failed

- message = -
-
-

Unable to send email.

-
-

Sign in

-
- break case 'CredentialsSignin': - statusCode = 403 - heading =

Sign in failed

- message = -
-
-

Check the details you provided are correct.

-
-

Sign in

-
- break + // These messages are displayed in line on the sign in page + res.status(302).setHeader('Location', `${signinPageUrl}?error=${error}`) + res.end() + return false case 'Configuration': statusCode = 500 heading =

Server error

diff --git a/src/server/pages/index.js b/src/server/pages/index.js index e4b7c67f..61b54d6f 100644 --- a/src/server/pages/index.js +++ b/src/server/pages/index.js @@ -18,6 +18,7 @@ function render (req, res, page, props, done) { break case 'error': html = error({ ...props, res }) + if (html === false) return done() break default: html = error(props) diff --git a/src/server/pages/signin.js b/src/server/pages/signin.js index 9bea8f6d..98193a0e 100644 --- a/src/server/pages/signin.js +++ b/src/server/pages/signin.js @@ -2,7 +2,7 @@ import { h } from 'preact' // eslint-disable-line no-unused-vars import render from 'preact-render-to-string' export default ({ req, csrfToken, providers, callbackUrl }) => { - const { email } = req.query + const { email, error } = req.query // We only want to render providers const providersToRender = providers.filter(provider => { @@ -18,8 +18,37 @@ export default ({ req, csrfToken, providers, callbackUrl }) => { } }) + let errorMessage + if (error) { + switch (error) { + case 'Signin': + case 'OAuthSignin': + case 'OAuthCallback': + case 'OAuthCreateAccount': + case 'EmailCreateAccount': + case 'Callback': + errorMessage =

Try signing with a different account.

+ break + case 'OAuthAccountNotLinked': + errorMessage =

To confirm your identity, sign in with the same account you used originally.

+ break + case 'EmailSignin': + errorMessage =

Check your email address.

+ break + case 'CredentialsSignin': + errorMessage =

Sign in failed. Check the details you provided are correct.

+ break + default: + errorMessage =

Unable to sign in.

+ break + } + } + return render(
+ {errorMessage &&
+ {errorMessage} +
} {providersToRender.map((provider, i) =>
{provider.type === 'oauth' && diff --git a/www/src/css/buttons.css b/www/src/css/buttons.css index b074b154..8f0050ff 100644 --- a/www/src/css/buttons.css +++ b/www/src/css/buttons.css @@ -17,7 +17,7 @@ html[data-theme="dark"] .button:active { .button--primary, .button--primary:hover{ - background: linear-gradient(0deg, var(--ifm-color-primary-darkest) 0%, var(--ifm-color-primary-lighter) 100%) !important; + background: linear-gradient(0deg, #157efb 0%, var(--ifm-color-primary) 100%) !important; color: #fff; } @@ -32,5 +32,5 @@ html[data-theme="dark"] .button:active { html[data-theme="dark"] .button.button--secondary.button--outline { background: linear-gradient(0deg, #000000 0%, #222222 100%) !important; - color: #eeee !important + color: #eee !important } \ No newline at end of file