├── .eslintrc ├── .gitignore ├── README.md ├── components ├── AuthContent.tsx ├── CreatePostForm.tsx ├── Header.tsx ├── Layout.tsx ├── LogInForm.tsx ├── Nav.tsx ├── ProfileForm.tsx ├── SendPasswordResetEmailForm.tsx ├── SetPasswordForm.tsx ├── SignUpForm.tsx └── UnAuthContent.tsx ├── hooks └── useAuth.tsx ├── lib └── apolloClient.ts ├── next-env.d.ts ├── package-lock.json ├── package.json ├── pages ├── _app.tsx ├── create-post.tsx ├── forgot-password.tsx ├── index.tsx ├── log-in.tsx ├── log-out.tsx ├── members.tsx ├── profile.tsx ├── set-password.tsx └── sign-up.tsx ├── public ├── favicon.ico └── vercel.svg ├── styles ├── Home.module.css └── globals.css └── tsconfig.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Headless WordPress Authentication with Native Cookies 2 | 3 | Next.js app code for this blog post: 4 | https://developers.wpengine.com/blog/headless-wordpress-authentication-native-cookies/ 5 | 6 | This is a Next.js application that shows how to authenticate users using WordPress' own native auth cookies. 7 | 8 | ## About its usage on Safari & iOS ⚠️ 9 | 10 | Apple doesn't allow cross-site cookies, making the login impossible if you are hosting the app & WordPress on **separate domains**. 11 | 12 | The **short-term** solution is to disable this option from Safari settings: 13 | 14 | `Safari > Settings > Site tracking > Prevent Cross-Site Tracking`. 15 | 16 | It will allow you to use the app, but it won't fix the issue for all other Safari / iOS users... 17 | 18 | 19 | The **long-term** solution is to host both the WordPress (back-end) & the webapp (front-end) on the **same domain** (e.g. each one on a different sub-domain). 20 | 21 | See this related [issue](https://github.com/kellenmace/headless-wordpress-authentication-native-cookies/issues/4) for more informations. 22 | -------------------------------------------------------------------------------- /components/AuthContent.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, ReactNode } from "react"; 2 | import { useRouter } from "next/router"; 3 | 4 | import useAuth from "../hooks/useAuth"; 5 | 6 | export default function AuthContent({ children }: { children: ReactNode }) { 7 | const { loggedIn, loading } = useAuth(); 8 | const router = useRouter(); 9 | 10 | // Navigate unauthenticated users to Log In page. 11 | useEffect(() => { 12 | if (!loading && !loggedIn) { 13 | router.push('/log-in'); 14 | } 15 | }, [loggedIn, loading, router]); 16 | 17 | if (loggedIn) { 18 | return <>{children}; 19 | } 20 | 21 | return

Loading...

; 22 | } 23 | -------------------------------------------------------------------------------- /components/CreatePostForm.tsx: -------------------------------------------------------------------------------- 1 | import { useMutation, gql } from "@apollo/client"; 2 | 3 | const CREATE_POST = gql` 4 | mutation createPost($title: String!, $content: String!) { 5 | createPost(input: { 6 | title: $title 7 | content: $content 8 | status: PUBLISH 9 | }) { 10 | post { 11 | databaseId 12 | } 13 | } 14 | } 15 | `; 16 | 17 | export default function CreatePostForm() { 18 | const [createPost, { data, loading, error }] = useMutation(CREATE_POST); 19 | const wasPostCreated = Boolean(data?.createPost?.post?.databaseId); 20 | 21 | function handleSubmit(event: React.FormEvent) { 22 | event.preventDefault(); 23 | const data = new FormData(event.currentTarget); 24 | const values = Object.fromEntries(data); 25 | createPost({ 26 | variables: values 27 | }).catch(error => { 28 | console.error(error); 29 | }); 30 | } 31 | 32 | if (wasPostCreated) { 33 | return ( 34 |

Post successfully created.

35 | ); 36 | } 37 | 38 | return ( 39 |
40 |
41 | 42 | 48 | 49 |