validation

This commit is contained in:
Karol Wypchlo 2021-02-08 16:52:40 +01:00
parent 18c056ae52
commit c803b2a62f
6 changed files with 208 additions and 40 deletions

View File

@ -13,6 +13,7 @@
"autoprefixer": "^10.2.4",
"dayjs": "^1.10.4",
"express-jwt": "^6.0.0",
"formik": "^2.2.6",
"jwks-rsa": "^1.12.2",
"next": "^10.0.6",
"postcss": "^8.2.4",
@ -21,6 +22,7 @@
"react": "17.0.1",
"react-dom": "17.0.1",
"swr": "^0.4.1",
"tailwindcss": "^2.0.2"
"tailwindcss": "^2.0.2",
"yup": "^0.32.8"
}
}

View File

@ -0,0 +1,36 @@
export default function Message({ title, items = [] }) {
return (
<div className="rounded-md bg-red-50 p-4">
<div className="flex">
<div className="flex-shrink-0">
{/* Heroicon name: solid/x-circle */}
<svg
className="h-5 w-5 text-red-400"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
aria-hidden="true"
>
<path
fillRule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clipRule="evenodd"
/>
</svg>
</div>
<div className="ml-3">
{title && <h3 className="text-sm font-medium text-red-800">{title}</h3>}
{items.length > 0 && (
<div className={`${title ? "mt-2" : ""} text-sm text-red-700`}>
<ul className={`${items.length > 1 ? "list-disc pl-5 space-y-1" : ""}`}>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)}
</div>
</div>
</div>
);
}

View File

@ -1,12 +1,18 @@
import Link from "next/link";
import { Configuration, PublicApi } from "@ory/kratos-client";
import { useFormik } from "formik";
import config from "../../config";
import Message from "../../components/Form/Message";
const kratos = new PublicApi(new Configuration({ basePath: config.kratos.public }));
export async function getServerSideProps(context) {
const flow = context.query.flow;
if (process.env.NODE_ENV === "development") {
return { props: { flow: require("../../../stubs/login.json") } };
}
// The flow is used to identify the login and registration flow and
// return data like the csrf_token and so on.
if (!flow || typeof flow !== "string") {
@ -59,8 +65,9 @@ export default function Login({ flow }) {
...fieldProps[field.name],
}))
.sort((a, b) => (a.position < b.position ? -1 : 1));
console.log(flow);
const formik = useFormik({
initialValues: fields.reduce((acc, field) => ({ ...acc, [field.name]: field.value }), {}),
});
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
@ -87,6 +94,7 @@ export default function Login({ flow }) {
if you don't have one yet
</p>
</div>
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
<form
@ -108,38 +116,37 @@ export default function Login({ flow }) {
type={field.type}
autoComplete={fieldProps[field.name]}
required={field.required}
value={field.value || undefined}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values[field.name]}
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-green-500 focus:border-green-500 sm:text-sm"
/>
</div>
{field.errors && field.errors.length > 0 && (
<div className="mt-2">
<Message items={field.errors.map(({ message }) => message)} />
</div>
)}
</div>
))}
<div className="flex items-center justify-between">
{/* <div className="flex items-center">
<input
id="remember_me"
name="remember_me"
type="checkbox"
className="h-4 w-4 text-green-600 focus:ring-green-500 border-gray-300 rounded"
/>
<label htmlFor="remember_me" className="ml-2 block text-sm text-gray-900">
Remember me
</label>
</div> */}
<div className="text-sm">
<Link href="/recovery">
<a className="font-medium text-green-600 hover:text-green-500">Forgot your password?</a>
</Link>
</div>
</div>
<div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
>
Sign in
</button>
<Link href="/recovery">
<a className="text-sm font-medium text-green-600 hover:text-green-500">Forgot your password?</a>
</Link>
</div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
>
Sign in
</button>
{flow.methods.password.config.errors.length > 0 && (
<Message items={flow.methods.password.config.errors.map(({ message }) => message)} />
)}
</form>
</div>
</div>

View File

@ -1,12 +1,18 @@
import Link from "next/link";
import { Configuration, PublicApi } from "@ory/kratos-client";
import { useFormik } from "formik";
import config from "../../config";
import Message from "../../components/Form/Message";
const kratos = new PublicApi(new Configuration({ basePath: config.kratos.public }));
export async function getServerSideProps(context) {
const flow = context.query.flow;
if (process.env.NODE_ENV === "development") {
return { props: { flow: require("../../../stubs/registration.json") } };
}
// The flow is used to identify the login and registration flow and
// return data like the csrf_token and so on.
if (!flow || typeof flow !== "string") {
@ -69,8 +75,12 @@ export default function Registration({ flow }) {
...fieldProps[field.name],
}))
.sort((a, b) => (a.position < b.position ? -1 : 1));
const formik = useFormik({
initialValues: fields.reduce((acc, field) => ({ ...acc, [field.name]: field.value }), {}),
});
console.log(flow);
console.log(fields);
console.log("fieldProps", fieldProps);
return (
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
@ -114,24 +124,34 @@ export default function Registration({ flow }) {
<div>
<input
id={field.name}
name={field.name}
name={`['${field.name}']`}
type={field.type}
autoComplete={fieldProps[field.name]}
autoComplete={field.autoComplete}
required={field.required}
value={field.value || undefined}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values[field.name]}
className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-green-500 focus:border-green-500 sm:text-sm"
/>
{field.errors && field.errors.length > 0 && (
<div className="mt-2">
<Message items={field.errors.map(({ message }) => message)} />
</div>
)}
</div>
</div>
))}
<div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
>
Sign up
</button>
</div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500"
>
Sign up
</button>
{flow.methods.password.config.errors.length > 0 && (
<Message items={flow.methods.password.config.errors.map(({ message }) => message)} />
)}
</form>
</div>
</div>

View File

@ -0,0 +1,45 @@
{
"id": "bda73c77-1e21-4bfd-b85a-322fce2e4576",
"expires_at": "2020-01-28T13:48:04.690715Z",
"issued_at": "2020-01-28T13:38:04.690732Z",
"request_url": "http://127.0.0.1:4455/auth/browser/login",
"methods": {
"oidc": {
"method": "oidc",
"config": {
"action": "http://127.0.0.1:4455/.ory/kratos/public/auth/browser/methods/oidc/auth/bda73c77-1e21-4bfd-b85a-322fce2e4576",
"method": "POST",
"fields": [
{
"name": "csrf_token",
"type": "hidden",
"required": true,
"value": "QJreyXtUD4oUSJfGNjA/+6ydsQyq0o/rfTL6QK86VadVFg6mwgX5x1QHVQ6uRqKxmwAcavQup3ILCSwl7ke97g=="
}
]
}
},
"password": {
"method": "password",
"config": {
"action": "http://127.0.0.1:4455/.ory/kratos/public/auth/browser/methods/password/login?request=bda73c77-1e21-4bfd-b85a-322fce2e4576",
"method": "POST",
"fields": [
{
"name": "csrf_token",
"type": "hidden",
"required": true,
"value": "M1gAKA8fIhw4JOpQ/5m9mKARBAvKhzWkyhbxjtZNLG8m1NBHtk7UUXhrKJhn7yDSl4ypbZR7HT28LSfrlzDEJg=="
},
{ "name": "identifier", "type": "text", "required": true, "value": "asfdasdffads" },
{ "name": "password", "type": "password", "required": true }
],
"errors": [
{
"message": "The provided credentials are invalid. Check for spelling mistakes in your password or username, email address, or phone number."
}
]
}
}
}
}

View File

@ -0,0 +1,58 @@
{
"id": "dbff7b96-8116-42c5-8624-f9fb28f1db15",
"expires_at": "2020-01-28T13:49:01.2274112Z",
"issued_at": "2020-01-28T13:39:01.2274261Z",
"request_url": "http://127.0.0.1:4455/auth/browser/registration",
"methods": {
"oidc": {
"method": "oidc",
"config": {
"action": "http://127.0.0.1:4455/.ory/kratos/public/auth/browser/methods/oidc/auth/dbff7b96-8116-42c5-8624-f9fb28f1db15",
"method": "POST",
"fields": [
{
"name": "csrf_token",
"type": "hidden",
"required": true,
"value": "xwb6A6iHdsguYwkAM6m3jj196E7TcmiWpAavIRxuAgXSiipsEdaAhW4sy8ir3yrECuBFKI2OQA/SPXlEXRPqTA=="
}
]
}
},
"password": {
"method": "password",
"config": {
"errors": [
{
"message": "The provided credentials are invalid. Check for spelling mistakes in your password or username, email address, or phone number."
}
],
"action": "http://127.0.0.1:4455/.ory/kratos/public/auth/browser/methods/password/registration?request=dbff7b96-8116-42c5-8624-f9fb28f1db15",
"method": "POST",
"fields": [
{
"name": "csrf_token",
"type": "hidden",
"required": true,
"value": "xLg4B9WnuC0Ue+j9ay5EQvleaJpOl0H9xJJ7W3+Bwv7RNOhobPZOYFQ0KjXzWNkIzsPF/BBraWSyqa0+Pvwqtw=="
},
{
"name": "password",
"type": "password",
"required": true,
"errors": [{ "message": "password: Is required" }]
},
{
"name": "traits.email",
"type": "text",
"value": "",
"errors": [
{ "message": "traits.email: String length must be greater than or equal to 3" },
{ "message": "traits.email: Does not match format 'email'" }
]
}
]
}
}
}
}