Public/b2b pages

Introduction

Regular pages will be restricted with the erply back-office sessions, this means that the pages cannot be viewed without a valid erply user session.

We can use the b2b/public sessions to set pages to no use the back-office session. For that we have 2 options:

  1. Public session - these sessions are completely public and do not require a user to log in at all.
  2. Protected session (app user) - pages that can only be viewed by logged in app user. App users are special users that are managed in the application itself.

Permission

Public pages are protected by 3 different methods.

  1. Erply user group permissions - on installation a user group is created goerp-app-users-group, all data fetches to erply api’s will use the rights of this group.
  2. Dynamic api request whitelist - a list of calls that are allowed with the dynamic api feature.
  3. Parameter whitelist - a list of parameters that we allow to be used.

Dynamic api request whitelist

The whitelist is under the ‘Publish settings’ tab in the editor view.

Publish settings Publish settings

When enabled the dynamic requests would need to be added to the page

Request whitelist Request whitelist

Parameters whitelist

The following error is given when on public pages we send a parameter that is not added to the whitelist.

Parameter whitelist error Parameter whitelist error

The whitelist can be accessed under the ‘Publish settings’ in the editor view. The parameter whitelist counts in presets and request the whitelist (when used) so the values there do not need to be duplicated here.

Publish settings Publish settings

When enabled the used parameters need to be filled to the list of the page.

Parameter whitelist Parameter whitelist

If the permissions were previously set to the aliases then we can also use the ‘Import from alias’ button to automatically fill them based on the alias settings.

As of 1.234.5+ disabling of these features is not recommended and will be disabled for new templates.

Pattern matching rules for parameter whitelist

The whitelist supports pattern matching for the parameters. That would be very helpful when working with the json that contains or generates arrays. At the moment pattern matching is supported for digits <%d%> and string <%s%>.

To define a placeholder just put a <%d%> in the place where actual parameter name may have a number. Can be defined as many placeholders per parameter as needed.

For example, this rule:

KvsApi.Api.Json.saveData.string.entries.<%d%>.value.products.<%d%>.code

will much any of the following parameters:

KvsApi.Api.Json.saveData.string.entries.1.value.products.1.code
KvsApi.Api.Json.saveData.string.entries.2.value.products.1.code
KvsApi.Api.Json.saveData.string.entries.1.value.products.2.code
KvsApi.Api.Json.saveData.string.entries.2.value.products.2.code

Subsections of Public/b2b pages

Protected

Introduction

Protected pages can only be accessed with a logged in app user. App users are special app specific users that can be set up to access specific applications.

Login and registration is up to the specific applications. Either it allows user registration from a public page or user creation is handled in some admin panel instead.

Note that app users cannot exist without an application, and the users cannot access or log in if they are not associated with an existing application (as opposed to erply account users). These user sessions cannot access erply back-office applications and regular pages.

Public routes always are prefixed with either public/ or b2b/ (except on custom domains).

Setup

Set the page to allow b2b access to set this protection level.

This can be configured in the template editor mode under the ‘Publish setting’ right menu.

Note

It is also possible to set an optional expiration date to the page (UTC unix value) after this timestamp has passed on the server the will no longer be accessible using the b2b endpoint. Use authentication config input set the value.

Also note that this setting is based on applications or auth domains.

Add protected configuration Add protected configuration

Each user requires the application domain to be able to create a valid session for the application. By default, the application domain is the applications uuid. You can use the ‘Authentication domain’ field to set a custom value - if the same value is set for multiple applications then the applications can share the users.

Use the ‘Redirect non-authenticated B2B users to page’ option to automatically redirect to login or error page invalid accesses.

User creation/registration

Users can either be created using a public registration form (for stores, b2b pages etc), or creation is managed internally within another page or application.

Manual app user creation

Here the creation and management of users would be handled by a protected page (either erply session or a different set of app users). Use the automat api endpoints to create the users and manage them. https://template-engine-eu10.erply.com/api/documentation/index.html#/app-user

Domains

Domains under the user determine what application pages the user can access.

Auth drivers

Auth drivers indicate what authentication methods the user is allowed to use to log in. Inbuilt methods: password, pin and openid

Open id

Open ID setup can be used to create custom sso methods for login. Each provider that is set up for the login page can then be used with the users to log in.

Creating a custom open id driver:

  1. In the login page template, navigate to the Publish settings -> OpenID Configuration tab.
  2. Use the ‘Add new configuration’ button to register a new open id provided (driver). Fill in the required fields and fields related to the provider. Note that the ‘Provider’ field will be used as the driver name. The ’email’ scope is also required as app users will be matched with the email.
  3. In the login command set the auth driver to the created one.
  4. In order for the users to be able to use it they would need to have the ‘ms_entra’ auth driver. Note that for open id auth drivers the value field will not be used.
  5. The jwksUrl endpoint needs to point to the public keys configuration.

This example assumes the open id configuration was given the provider name of ‘ms_entra’.

<form method="post">
    <input type="hidden" name="AutomatApi.B2BLoginInput.Redirect" value="b2b-2-members-page">
    <input type="hidden" name="AutomatApi.B2BLoginInput.AuthDriver" value="ms_entra">

    <button type="submit">
        Microsoft SSO
    </button>
</form>

There is no limit to the open id providers. You can set up multiple providers to the same auth provider as-well (multiple app’s in ms entra for example).

Public registration page

With this you can create public facing endpoint where users can be created by customers themselves.

Domains

With inbuilt forms the domain is always taken from the registration template.

Auth drivers

With inbuilt from the auth driver used is always ‘password’

Setting up public registration page

Create a public page that implements the automat api’s registration form. Use the Session.Customer.ID to detect if the user is already logged in.

Optionally, the AutomatApi.B2BLoginRegisterInput.LoginOnSuccess can be used to automatically login newly created user and AutomatApi.B2BLoginRegisterInput.Redirect to redirect the user to a specific page after successful login.

Check the automat api B2BLoginRegisterInput data source docs for additional available fields.

<!-- Read registration errors -->
<div class="my-error-container">
    {{ range .Data.Errors }}
    <span class="my-error-message">{{ . }}</span>
    {{ end }}
</div>

{{ if .Session.Customer.ID }}
    <h1>Already registered</h1>
{{ else }}
    <h1>Register a new user</h1>
    <form method="post">
        <input type="hidden" name="postActionEntity" value="B2BLoginRegisterInput">
        <input type="hidden" name="postActionRedirect" value="b2b-login-demo-page">

        <!-- Configure autologin and redirect -->
        <input type="hidden" name="AutomatApi.B2BLoginRegisterInput.LoginOnSuccess" value="1">
        <input type="hidden" name="AutomatApi.B2BLoginRegisterInput.Redirect" value="b2b-2-members-page">

        <br>
        <label for="firstName">First name</label>
        <input type="text" id="firstName" name="AutomatApi.B2BLoginRegisterInput.Firstname">

        <br>
        <label for="lastName">Last name</label>
        <input type="text" id="lastName" name="AutomatApi.B2BLoginRegisterInput.Lastname">

        <br>
        <label for="email">Email</label>
        <input type="text" id="email" name="AutomatApi.B2BLoginRegisterInput.Username">

        <br>
        <label for="password">Password</label>
        <input type="password" id="password" name="AutomatApi.B2BLoginRegisterInput.Password">

        <br>
        <button type="submit">Register</button>
    </form>
{{ end }}

Setting up public login page

Create a public page that implements the automat api’s login form. Use the Session.Customer.ID to detect if the user is already logged in.

Optionally the AutomatApi.B2BLoginInput.Redirect can be used to redirect the user to a specific page after successful login

<!-- Read login errors -->
<div class="my-error-container">
    {{ range .Data.Errors }}
    <span class="my-error-message">{{ . }}</span>
    {{ end }}
</div>

{{ if .Session.Customer.ID }}
    <p>Welcome {{ .Session.Customer.FirstName }}</p>
{{ else }}
    <form method="post">
        <label for="username">Email</label>
        <input type="text" id="username" name="AutomatApi.B2BLoginInput.Username"/>

        <br>

        <label for="password">Password</label>
        <input type="password" id="password" name="AutomatApi.B2BLoginInput.Password"/>

        <br>
        <button type="submit">Login</button>
    </form>
{{ end }}

Public

Introduction

Public pages are accessible without any login.

Setup

This can be added in the template edit view and enabling the “Allow B2B and public access” setting.

Note

It is also possible to set an optional expiration date to the page (UTC unix value) after this timestamp has passed on the server the will no longer be accessible using the b2b endpoint. Use authentication config input set the value.

Add public configuration Add public configuration

These routes can be accessed by prefixing the routes with /public/

Example:

  1. automat-eu.erply.com/104146/en/my-test-page
  2. automat-eu.erply.com/public/104146/en/my-test-page

Using dynamics

Starting from version 1.246.0, GoErp allows to use dynamic api features while creating register, login and others b2b authentication calls. This allows to create multiple requests and chain data between them, including authorization calls.

Note

Those API calls works properly only inside GoErp templates, because b2b (public) authentication depends on the template configuration, therefore all b2b API endpoints requires b2bKey to be sent in the request header. This key is available inside session and could be chained to the API request header.

More about b2b API calls read in the Automat API documentation.

Registration page

A simple registration page using API and dynamics. All page configuration steps remains same as for model based b2b authentication.

<h1>Register page</h1>
<form method="post">
   <input type="hidden" name="AutomatApi.Api.Post.register" value="v1/b2b/register-user">
   <!-- Use chaining to pass b2b key to the header -->
   <input type="hidden" name="AutomatApi.Api.Header.register.<-b2bKey" value="Session.key">
   <!-- Use .Tools.B2bAuthDomain to get the domain -->
   <input type="hidden" name="AutomatApi.Api.Json.register.string.domain" value="{{ .Tools.B2bAuthDomain }}">
   <!--  Setup redirect on succeed, if needed. It will be triggered only if all calls in this 
   template are successful. So, if registration fails, we stay on this page, very useful. -->
   <input type="hidden" name="Form.Redirect" value="10-b2b-in-page">
   
   <fieldset>
      <label for="firstname">Firstname:</label>
      <input id="firstname" name="AutomatApi.Api.Json.register.string.firstname" value="">
      <label for="lastname">Lastname:</label>
      <input id="lastname" name="AutomatApi.Api.Json.register.string.lastname" value="">
   </fieldset>
   <fieldset>
      <label for="username">Username:</label>
      <input id="username" name="AutomatApi.Api.Json.register.string.username" value="">
      <label for="password">Password:</label>
      <input id="password" name="AutomatApi.Api.Json.register.string.password" value="">
   </fieldset>
    <!-- Login would be performed automatically only if this parameter set to 1 -->
   <input type="hidden" name="AutomatApi.Api.Json.register.string.triggerLoginAfterRegister" value="1">
   <button type="submit">Register</button>
</form>
Note

Please note that if the triggerLoginAfterRegister parameter is not passed or set to 0, the user will be registered but not logged in. Although, Form.Redirect will be triggered anyway and try to access the protected area (10-b2b-in-page in this case). Therefore, while user not logged in, the 10-b2b-in-page page configuration will redirect to the login page (if configured). So, this could be confusing.

Login page

Here we will just use the username and password, and redirect to the protected area when successful.

<h1>Login page</h1>
<form method="post">
   <input type="hidden" name="AutomatApi.Api.Post.login" value="v1/b2b/login">
   <!-- Use chaining to pass b2b key to the header -->
   <input type="hidden" name="AutomatApi.Api.Header.login.<-b2bKey" value="Session.key">
   <!-- Use .Tools.B2bAuthDomain to get the domain -->
   <input type="hidden" name="AutomatApi.Api.Json.login.string.domain" value="{{ .Tools.B2bAuthDomain }}">
   <!--  Setup redirect on succeed, if needed. It will be triggered only if all calls in this 
   template are successful. So, if login fails, we stay on this page, very useful. -->
   <input type="hidden" name="Form.Redirect" value="10-b2b-in-page">
   <fieldset>
      <label for="username">Username:</label>
      <input id="username" name="AutomatApi.Api.Json.login.string.username" value="">
      <label for="password">Password:</label>
      <input id="password" name="AutomatApi.Api.Json.login.string.password" value="">
   </fieldset>
   <button type="submit">Login</button>
</form>

Samples

In the following sample we have a registration, login and a members area. This shows how we restrict the members area to only read results for the logged in customer.

Page setup

Note

The public features only work if the registration, login and any members pages belong to the same application or auth domain.

Registration page

A simple registration page with minimal inputs.

<!DOCTYPE html>
<html>
<body>
    <h2>Register a new user</h2>
    <form method="post">
        <input type="hidden" name="postActionEntity" value="B2BLoginRegisterInput">
        <input type="hidden" name="postActionRedirect" value="b2b-2-login-page">

        <div class="user-box">
            <input type="text" name="AutomatApi.B2BLoginRegisterInput.Firstname" required="">
            <label>Firstname</label>
        </div>
        <div class="user-box">
            <input type="text" name="AutomatApi.B2BLoginRegisterInput.Lastname" required="">
            <label>Lastname</label>
        </div>
        <div class="user-box">
            <input type="text" name="AutomatApi.B2BLoginRegisterInput.Username" required="">
            <label>Email</label>
        </div>
        <div class="user-box">
            <input type="password" name="AutomatApi.B2BLoginRegisterInput.Password" required="">
            <label>Password</label>
        </div>

        <!-- Read possible registration errors -->
        <div>
            {{ range .Data.Errors }}
                <span>{{ . }}</span>
            {{ end }}
        </div>

        <button type="submit">Register</button>
    </form>
</body>
</html>

For publish settings we enable the ‘Allow B2B and public access’ checkbox

Also since registration allows more fields to be used we also need to fill in the parameter whitelist of the value we will allow to be used, in this case we will fill the ones we have defined in the form.

Add public configuration Add public configuration

Login page

Here we will just use the username and password, and redirect to the members area when successful.

<!DOCTYPE html>
<html>
<body>
    {{ if .Session.Customer.ID }}
        <h2>Already logged on!</h2>
    {{ else }}
    <h2>Login</h2>
    <form method="post">
        <input type="hidden" name="AutomatApi.B2BLoginInput.Redirect" value="b2b-2-members-page">

        <div class="user-box">
            <input type="text" name="AutomatApi.B2BLoginInput.Username" required="">
            <label>Username</label>
        </div>
        <div class="user-box">
            <input type="password" name="AutomatApi.B2BLoginInput.Password" required="">
            <label>Password</label>
        </div>
        
        <div class="my-error-container">
            {{ range .Data.Errors }}
            <span class="my-error-message">{{ . }}</span>
            {{ end }}
        </div>
        
        <button type="submit">Login</button>
    </form>
    {{ end }}
</body>
</html>

For publish settings we enable the ‘Allow B2B and public access’ checkbox.

Login model parameters are automatically whitelisted, so we should not need to fill them here.

Members page

On the members page we will generate a simple list of documents for the currently logged on member. We use the preset to read session id to the request and prevent it from being adjusted via any parameters.

<!DOCTYPE html>
<html>
<body>
    <h1>Members page</h1>
    <form method="post">
        <input type="hidden" name="AutomatApi.B2BLogoutInput.Logout" value="1">
        <button type="submit">Logout</button>
    </form>
    <h2>Welcome {{ .Session.Customer.FirstName }} to the members area</h2>

    <!-- Erply api response data in the 'records' field -->
    <h2>My orders</h2>
    <form method="POST">
        <!-- Request definition -->
        <input type="hidden" name="ErplyApi.Api.Post.getDocs" value="getSalesDocuments" data-preset-val="getSalesDocuments">
        <button type="submit">Reload orders</button>
    </form>

    <ul>
        {{ $salesDocs := (.Data.ErplyApi.Api.Requests.getDocs.Response.Get "records").Array }}
    
        {{ if $salesDocs }}
            {{ range $salesDocs }}
            <li>{{ .Get "id" }} / {{ .Get "type" }} / {{ .Get "clientName" }}</li>
            {{ end }}
        {{ else }}
            <li>You currently have no orders!</li>
        {{ end }}
    </ul>
</body>
</html>
Note

By default all public access groups do not have access to read documents. This right needs to be given under the public user groups (starting with ‘app_public’ and ‘app_b2b’) by the account administrator in the backoffice.

Every application or authentication domain will have a separate user group with rights assigned to them.

For publish settings we enable the ‘Allow B2B access’ checkbox as we will only want logged in members to access it.

We also set the redirection to the name of the login page, so whenever its being accessed without a proper session it will be automatically redirected.

Add permissions Add permissions

We are also using dynamic api here, so we will add the ErplyApi.Api.Post.getDocs -> getSalesDocuments to the request whitelist.

Under URl configuration we add the preset ErplyApi.Api.PostParam.getDocs.<-clientID : Session.customer.ID This will write the current session customer id to the request when it is being done, since we do not allow the parameter to be adjusted in the parameters list then it cannot be changed to anything else.

Add preset chain Add preset chain

Workflow

To test it:

  1. Register a new user
  2. Login with the created customer
  3. The members area only displays the members sales documents (use backoffice to create them or create a new page that creates the documents for the member using the same method)