4

I have built a React app that uses JSON:API to pull in data, but now I want to implement user registration.

JSON:API doesn't do user registration because it isn't a CRUD operation.

However, the links above explain that core provides a REST endpoint for handling user registration.

The documentation gives this example:

curl \
  --header "Content-type: application/json" \
  --request POST http://drupal.d8/user/register

But this example seems incomplete-- for example, where is the username?

My website is configured with the Email Registration module and only an email address is required to register, but where do I submit the email address?

EDIT: If you're answering this question, I will accept an answer that uses core's username/email address combo for registration; it doesn't have to be specific to the Email Registration module. The problem is that I can't find an example of how to register users with JSON:API, and there is no documentation.

Here's the code I tried in React:

UserRegistrationForm.tsx

import * as React from 'react';
import { useForm } from 'react-hook-form';
import { IonButton } from '@ionic/react';
import { baseUrl, basicHeaders } from '../utils/globals';
import handleErrors from '../utils/fetchHandleErrors';

interface InterfaceRegistrationFormInput {
  email: string;
}

const UserRegistrationForm: React.VFC = () => {
  const { register, handleSubmit, errors } = useForm<InterfaceRegistrationFormInput>();

  const onSubmit = (formData: InterfaceRegistrationFormInput) => {
    fetch(`${baseUrl}/user/register`, {
      method: 'POST',
      headers: basicHeaders,
      body: formData,
    })
      .then(handleErrors)
      .catch((error) => Promise.reject(new Error(error)));
  };

  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <label htmlFor="email">
          Email address
          <br />
          <input
            name="email"
            type="email"
            ref={register({ required: true })}
          />
        </label>
        <IonButton type="submit">Register</IonButton>
      </form>
    </>
  );
};
3
  • Did you try what the example after that had for curl? Ex: --data '{"name":"admin", "pass":"admin"}'
    – Kevin
    Feb 17, 2021 at 15:56
  • @Kevin Simply using POST on example.com/user/register as suggested in the documentation returns the registration page itself. So there's no error to see what is going wrong. Feb 17, 2021 at 17:17
  • Did you try : curl \ --user uname:password \ --header 'Accept: application/json' \ --header 'Content-type: application/json' \ --request POST http://drupal8.dev/user/register \ --data-binary @post_data.json where post_data.json file has your user data. Feb 18, 2021 at 6:48

4 Answers 4

4
+200

To register user using core Rest Module you need 5 steps:

Step 1: Download and enable Rest UI module, this module provides a user interface for administrating Rest Endpoints. Using this module's configuration (admin/config/services/rest) Enable User Registration

Enable User Registration Endpoint

Step 2: Then in account setting configuration you should allow user to create account, and if you want user to be added using user name and password you will need to uncheck require email verification else leave it checked. Allow site users to create account on the site

Step 3: Now you need to allow anonymous user to access user registration (admin/people/permissions/anonymous):

allow anonymous user to register using rest

Step 4: Another step to take is to get a xcsrf token from drupal cause you need to use a post method for user creation and post methods are unsafe methods, so drupal protects them using token, to get a XCSRF Token simply call /session/token endpoint.

Step 5: Now final step is to ask for user creation:

Method: POST

Endpoint: user/register?_format=json

Header:

Content-Type: application/json
Accept: application/json
X-CSRF-Token: "XCSRF TOKEN PROVIDED BY DRUPAL"

Body:

{
  "name": [{"value": "USERNAME"}],
  "mail": [{"value": "EMAIL"}],
  "pass":[{"value": "PASSWORD"}]
}

and that's it.

PS: Remember that if you have not unchecked "require email verification" in account settings form then you should exclude pass from your request body. in this case user needs to navigate to your drupal site for email verification which in case of decoupled scenario it sounds wired. so I highly recommend to uncheck this option in account settings configuration.

Another PS: If you need to require user to confirm his/her email, then you can use a simple solution, like add a verify code to user (prevent user access to this field using Field Permissions module) then in hook_user_presave you can assign a code to it, send user an email containing this code and finally deactivate user) , finally you will need a custom Rest endpoint so user can fill a form containing email field and code and if code matches the one you sent you can activate user's account.

10
  • After adding RestUI, allowing visitors to create accounts, giving anonymous permission to POST on user registration, when I go to www.example.com/user/register?_format=json, I get this error: {"message":"Not acceptable format: json"} Feb 22, 2021 at 12:53
  • navigate to /admin/config/services/rest/resource/user_registration/edit and make sure json is enabled as accepted request format, also you might need to change Granularity to Method Feb 22, 2021 at 17:52
  • I tried with Granularity: Method, Accepted request formats: json, and Authentication providers: oauth2, cookie. Still the same error. Feb 23, 2021 at 3:49
  • Has you tried that on postman or you just tried it in your react code? what is your drupal minor version? Feb 23, 2021 at 21:51
  • Tried on postman and in my own React code; I get the same error either way. Drupal 8.9.13, REST UI 1.19. Feb 24, 2021 at 0:59
2

This is surprisingly difficult given all the good press decoupled Drupal has gotten over the past few years-- user registration in Core has bugs that make it impossible to use without patching.

Building on Alireza Tabatabaeian's answer, this answer is specifically for

  1. Using the Email Registration module.
  2. Requiring users to verify their email addresses.
  3. Doing it in React/TypeScript (code example at the end)

Detailed steps

First, enable Rest UI.

Navigate to /admin/config/services/rest.

Enable User registration.

Configure as follows:

  • Granularity: Resource
  • Methods: POST
  • Accepted request formats: json
  • Authentication providers: cookie

Next, at /admin/people/permissions#module-rest, give the anonymous user Access POST on User Registration resource permission.

Testing on Postman

You should now be able to test on Postman.

Here's how to format the request.

  1. Select POST.
  2. For the url, use http://www.example.com/user/register?_format=json.
  3. For the headers:
  • Content-Type: application/json
  • Accept: application/json
  • X-CSRF-TOKEN: (use value from GET to http://www.example.com/session/token

For the request body:

raw format

And then use this value:

{
    "name":{"value":"test"},
    "mail":{"value":"test@example.com"}
}

You will need to add pass as well if you are not requiring email verification.

If you are requiring email verification, there is a Core bug that will block all users registered via REST and prevent email validation. You will need the patch from this issue.

React TypeScript Ionic code example

UserRegistrationForm.tsx

import * as React from 'react';
import { useForm } from 'react-hook-form';
import {
  IonButton, IonItem, IonLabel, IonInput,
} from '@ionic/react';
import { baseUrl, restHeaders } from '../utils/globals';
import handleErrors from '../utils/fetchHandleErrors';
import { fetchWithCSRFToken } from '../utils/fetch';
import ErrorSummary from '../utils/ErrorSummary';

interface InterfaceRegistrationFormInput {
  email: string;
}

const UserRegistrationForm: React.VFC = () => {
  const {
    register, handleSubmit, errors, setError,
  } = useForm<InterfaceRegistrationFormInput>();

  const onSubmit = (formData: any) => {
    const myRegData = {
      name: {
        value: Date.now(),
      },
      mail: {
        value: formData.mail,
      },
    };

    fetchWithCSRFToken(`${baseUrl}/user/register?_format=json`, {
      method: 'POST',
      headers: restHeaders,
      body: JSON.stringify(myRegData),
    })
      .then(handleErrors)
      .then((response) => {
        console.log('response', response);
      })
      .catch((error: any) => {
        if (error.message === 'Unprocessable Entity') {
          setError('email', {
            type: 'manual',
            message: 'You have already registered.',
          });
        } else {
          setError('email', {
            type: 'manual',
            message: 'Unknown error.',
          });
        }
      });
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <IonItem>
        <ErrorSummary errors={errors} />
        <IonLabel position="stacked">
          Email address
        </IonLabel>
        <IonInput
          name="mail"
          type="email"
          ref={register({ required: true }) as any}
        />
      </IonItem>
      <IonButton type="submit">Register</IonButton>
    </form>
  );
};

export default UserRegistrationForm;

globals.tsx

export const restHeaders: HeadersInit = new Headers({
  'Content-Type': 'application/json',
  Accept: 'application/json',
});

fetchHandleErrors.tsx

// Throw exceptions instead of fetch default behavior.
// https://www.tjvantoll.com/2015/09/13/fetch-and-errors/
export default function handleErrors(response: Response): Response {
  if (!response.ok) {
    throw new Error(response.statusText);
  }
  return response;
}

fetch.tsx

export const fetchWithCSRFToken = (fetchUrl: string, fetchOptions: any) => {
  if (!fetchOptions.headers.get('X-CSRF-Token')) {
    const csrfUrl = `${baseUrl}/session/token`;
    return fetch(csrfUrl)
      .then((response) => response.text())
      .then((csrfToken) => {
        // console.log('csrfToken', csrfToken);
        fetchOptions.headers.append('X-CSRF-Token', csrfToken);
        return fetch(fetchUrl, fetchOptions);
      });
  }
  return fetch(fetchUrl, fetchOptions);
};

ErrorSummary.tsx

import React from 'react';
import { ErrorMessage } from '@hookform/error-message';
import { FieldErrors } from 'react-hook-form';

type ErrorSummaryProps<T> = {
  errors: FieldErrors<T>;
};

// https://www.carlrippon.com/react-hook-form-validation-errors/
function ErrorSummary<T>({ errors }: ErrorSummaryProps<T>) {
  if (Object.keys(errors).length === 1) {
    return null;
  }
  return (
    <div className="error-summary">
      {Object.keys(errors).map((fieldName) => (
        <ErrorMessage errors={errors} name={fieldName as any} as="div" key={fieldName} />
      ))}
    </div>
  );
}

export default ErrorSummary;
0

This seems not possible without core's RESTful Web Services module enabled.

You need to specify the _format in the url to be routed right, like: /user/register?_format=hal_json

I assume your basicHeaders contain:

Accept: application/json
Content-Type: application/json

Plus make sure to give the permission Access POST on User registration resource to anonymous users.

And make the body look like:

{
 "_links":{"type":{"href":"${baseUrl}/rest/type/user/user"}},
 "mail":{
   "value":"user@example.com"
 }
}

(Assuming the email registration module re-uses the core's field name.)

You can also use _format=json and skip the "_links" JSON property.

Source another documentation page and the grand d.o. issue.

I updated also the documentation you linked. (Added ?_format=json)

5
  • When I try example.com/user/register?_format=json, I get this error: {"message":"Not acceptable format: json"} When I try example.com/user/register?_format=hal_json, I get a WSOD: Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException: Not acceptable format: hal_json in Drupal\Core\EventSubscriber\RenderArrayNonHtmlSubscriber->onRespond() (line 30 of core/lib/Drupal/Core/EventSubscriber/RenderArrayNonHtmlSubscriber.php). I uninstalled the Email Registration module and I get the same behavior. Feb 18, 2021 at 1:29
  • Also, I can't find the Access POST on User registration resource permission at /admin/people/permissions. I thought it might be on the REST module, so I enabled that, but I can't find a permission with a name like that. Feb 18, 2021 at 1:48
  • Okey -- sorry, I just copied this information together, but had no luck ;) Maybe you need to enable core's "RESTful Web Services" module, as this is not covered by JSON:API .. so the answer to your question is probably - no, you cannot do this by only using JSON:API, first you could also just try to enable the HAL module .. but I doubt it will do it ..
    – rémy
    Feb 18, 2021 at 2:05
  • Actually, you apparently do not need to enable REST, based on this comment from a Core maintainer: drupal.org/project/jsonapi/issues/2877549#comment-12087009 But, how exactly to do this is apparently not documented anywhere, hence my question here. Feb 18, 2021 at 2:09
  • I would need to X-DEBUG, maybe this is no longer possible ... but you can confirm, that it works with REST enabled ?
    – rémy
    Feb 18, 2021 at 21:56
0

There is a JSON:API User Resources module that provides an endpoint for doing user registration.

I couldn't get this to work with the Email Registration module, but it may be a good solution for others so I'll note it here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.