validation
This commit is contained in:
parent
18c056ae52
commit
c803b2a62f
|
@ -13,6 +13,7 @@
|
||||||
"autoprefixer": "^10.2.4",
|
"autoprefixer": "^10.2.4",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.4",
|
||||||
"express-jwt": "^6.0.0",
|
"express-jwt": "^6.0.0",
|
||||||
|
"formik": "^2.2.6",
|
||||||
"jwks-rsa": "^1.12.2",
|
"jwks-rsa": "^1.12.2",
|
||||||
"next": "^10.0.6",
|
"next": "^10.0.6",
|
||||||
"postcss": "^8.2.4",
|
"postcss": "^8.2.4",
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
"swr": "^0.4.1",
|
"swr": "^0.4.1",
|
||||||
"tailwindcss": "^2.0.2"
|
"tailwindcss": "^2.0.2",
|
||||||
|
"yup": "^0.32.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,12 +1,18 @@
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Configuration, PublicApi } from "@ory/kratos-client";
|
import { Configuration, PublicApi } from "@ory/kratos-client";
|
||||||
|
import { useFormik } from "formik";
|
||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
|
import Message from "../../components/Form/Message";
|
||||||
|
|
||||||
const kratos = new PublicApi(new Configuration({ basePath: config.kratos.public }));
|
const kratos = new PublicApi(new Configuration({ basePath: config.kratos.public }));
|
||||||
|
|
||||||
export async function getServerSideProps(context) {
|
export async function getServerSideProps(context) {
|
||||||
const flow = context.query.flow;
|
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
|
// The flow is used to identify the login and registration flow and
|
||||||
// return data like the csrf_token and so on.
|
// return data like the csrf_token and so on.
|
||||||
if (!flow || typeof flow !== "string") {
|
if (!flow || typeof flow !== "string") {
|
||||||
|
@ -59,8 +65,9 @@ export default function Login({ flow }) {
|
||||||
...fieldProps[field.name],
|
...fieldProps[field.name],
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => (a.position < b.position ? -1 : 1));
|
.sort((a, b) => (a.position < b.position ? -1 : 1));
|
||||||
|
const formik = useFormik({
|
||||||
console.log(flow);
|
initialValues: fields.reduce((acc, field) => ({ ...acc, [field.name]: field.value }), {}),
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
<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
|
if you don't have one yet
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
<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">
|
<div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||||
<form
|
<form
|
||||||
|
@ -108,38 +116,37 @@ export default function Login({ flow }) {
|
||||||
type={field.type}
|
type={field.type}
|
||||||
autoComplete={fieldProps[field.name]}
|
autoComplete={fieldProps[field.name]}
|
||||||
required={field.required}
|
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"
|
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>
|
</div>
|
||||||
|
|
||||||
|
{field.errors && field.errors.length > 0 && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<Message items={field.errors.map(({ message }) => message)} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<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">
|
<Link href="/recovery">
|
||||||
<a className="font-medium text-green-600 hover:text-green-500">Forgot your password?</a>
|
<a className="text-sm font-medium text-green-600 hover:text-green-500">Forgot your password?</a>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
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"
|
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
|
Sign in
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
|
{flow.methods.password.config.errors.length > 0 && (
|
||||||
|
<Message items={flow.methods.password.config.errors.map(({ message }) => message)} />
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Configuration, PublicApi } from "@ory/kratos-client";
|
import { Configuration, PublicApi } from "@ory/kratos-client";
|
||||||
|
import { useFormik } from "formik";
|
||||||
import config from "../../config";
|
import config from "../../config";
|
||||||
|
import Message from "../../components/Form/Message";
|
||||||
|
|
||||||
const kratos = new PublicApi(new Configuration({ basePath: config.kratos.public }));
|
const kratos = new PublicApi(new Configuration({ basePath: config.kratos.public }));
|
||||||
|
|
||||||
export async function getServerSideProps(context) {
|
export async function getServerSideProps(context) {
|
||||||
const flow = context.query.flow;
|
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
|
// The flow is used to identify the login and registration flow and
|
||||||
// return data like the csrf_token and so on.
|
// return data like the csrf_token and so on.
|
||||||
if (!flow || typeof flow !== "string") {
|
if (!flow || typeof flow !== "string") {
|
||||||
|
@ -69,8 +75,12 @@ export default function Registration({ flow }) {
|
||||||
...fieldProps[field.name],
|
...fieldProps[field.name],
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => (a.position < b.position ? -1 : 1));
|
.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 (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
<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>
|
<div>
|
||||||
<input
|
<input
|
||||||
id={field.name}
|
id={field.name}
|
||||||
name={field.name}
|
name={`['${field.name}']`}
|
||||||
type={field.type}
|
type={field.type}
|
||||||
autoComplete={fieldProps[field.name]}
|
autoComplete={field.autoComplete}
|
||||||
required={field.required}
|
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"
|
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>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div>
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
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"
|
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
|
Sign up
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
|
{flow.methods.password.config.errors.length > 0 && (
|
||||||
|
<Message items={flow.methods.password.config.errors.map(({ message }) => message)} />
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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'" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue