Skip to content

CodeCoupler UI Authentication

The system can be configured with an authentication module and an authentication application.

The module provides methods to login, logout, provide informations about the current user and its persistent configuration.

The application provides an UI to enter credentials and call the login method of the module.

To access the module and use the provided methods you can use the property auth of a System instance. To request a login and start the authentication application you can call the method login of a System instance.

Implement an Authentication Application

An authentication application is a regular CodeCoupler Application. The application receives at startup in addition to the options defined in the system configuration the following options:

module: Authentication Module

The module that should be used to call the login method.

message: String

An optional message that should be shown to the user.

The only thing the application has to do is to call at any time the method login of the given authentication module. If the login fails (an exception will be thrown) the user should be informed and he should retry the login.

If the login was successful the application must be closed (this.panel.close() or this.close()).

Implement an Authentication Module

To write a new stage class you have to extend the CodeCoupler class AuthBase. The base class provides methods for event handling:

on(event,handler), one(event,handler), off([event],[handler]), trigger(event,argments)

All other methods must be overriden by your class as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import { AuthModBase } from "@codecoupler/cc-ui";

export default class extends AuthModBase {
  //This is just an example how to implement a cache:
  #currentUser;
  #currentUserFetched;

  //If you want to override the constructor start with "super(options);"
  constructor(options) {
    super(options);
    #currentUser = null;
    #currentUserFetched = false;
  }

  //If called "getUser" try to fetch the current user and to check if the
  //login is still valid. The object you return is not specified. You can
  //return here whatever you want if the login is valid. If no user login
  //exists you have to return "null".
  //
  //The function should always resolve, never reject. In case of an error it
  //have to resolve with null.
  async getUser() {
    if (!this.#currentUserFetched) {
      try {
        this.#currentUser = await your_function_to_fetch_current_user();
      } catch (e) {
        this.#currentUser = null;
      }
      this.#currentUserFetched = true;
    }
    return this.#currentUser;
  }

  //Return an array with permissions. Each permission is represented by a string.
  async getPermissions() {
    if (!this.#currentUserFetched) await this.getUser();
    return this.#currentUser ? extract_permissions_from_user_object(this.#currentUser) : [];
  }

  //Return an array with permissions. Each permission is represented by a string.
  async getRoles() {
    if (!this.#currentUserFetched) await this.getUser();
    return this.#currentUser ? extract_roles_from_user_object(this.#currentUser) : [];
  }

  //Implement your logic to login. Use any argument signature you need.
  //Do not forget to call "this.trigger("login");"!
  async login(username, password) {
    try {
      await your_function_to_authenticate(username, password);
      this.trigger("login");
    } catch (e) {
      throw convert_error_object_to_string(e);
    }
  }

  //Implement your logic to logout.
  //Do not forget to call this.trigger("logout")
  logout() {
    this.#currentUser = null;
    this.trigger("logout");
  }

  //This is the method to save any configuration key/value-pairs to the user
  //object. This should be saved on the remote server. If not possible you
  //should always call "await super.saveConfig(key, value)" as fallback.
  async saveConfig(key, value) {
    await super.saveConfig(key, value);
  }

  //This is the method to get any configuration key/value-pairs from the user
  //object that have been saved with "saveConfig". If not possible you  should
  //always return the value from "await super.getConfig(key)" as fallback.
  async getConfig(key) {
    return await super.getConfig(key);
  }
}

Provided Applications and Modules

CodeCoupler provides the following applications and modules by default:

Authentication Application "Basic"

The application provides an UI to enter just a username and a password. The application is very flexible and can be adapted to your own needs.

The options that the application expects:

hooks: Object

An object to define hooks to adapt the behavior and appearance. See below for more details.

messages: Object

Define the used texts:

USERNAME, PASSWORD, LOGIN

The basic structure is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
+--------------------------------------+
| +----------------------------------+ |
| | Logo data-role="logo"            | |
| +----------------------------------+ |
| +----------------------------------+ |
| | Message data-role="message"      | |
| +----------------------------------+ |
| +----------------------------------+ |
| | Label data-role="username-label" | |
| +----------------------------------+ |
| +----------------------------------+ |
| | Input data-role="username"       | |
| +----------------------------------+ |
| +----------------------------------+ |
| | Label data-role="password-label" | |
| +----------------------------------+ |
| +----------------------------------+ |
| | Input data-role="password"       | |
| +----------------------------------+ |
| +----------------------------------+ |
| | Button data-role="submit"        | |
| +----------------------------------+ |
+--------------------------------------+

We go through the following process and call the following hooks:

  • Start the panel and build the HTML structure.
  • Call the hook init(panel).
  • If the hook did not return false:
  • Resize the panel.
  • Set focus to the username field.
  • Bind the Enter key to trigger the button click.
  • After the button was clicked:
  • Call the hook start().
  • If the hook did not return false:
    • Disable inputs elements and button. Start Button animation.
  • Call the login(username,password) method of the module.
  • If succeed:
    • Call the hook done().
    • Close the application.
  • If failed:
    • The application expects that the thrown error is just a plain string. If not a generic error message will be generated.
    • Call the hook fail(errorMessage,errorObject).
    • If the hook did not return false:
    • Enable inputs elements and button. Stop Button animation.
    • Show error message.
    • Resize and reposition panel.

Here an example of options which change the logo, hide the labels and translates all other messages:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  hooks: {
    init: function (panel) {
      var $content = $(panel.content);
      var $logo = $content.find("[data-role=logo]");
      $logo.replaceWith('<img src="logo.png" width="100" height="100">');
      $content.find("[data-role=username-label]").hide();
      $content.find("[data-role=password-label]").hide();
    }
  },
  messages: {
    USERNAME: "Benutzername",
    PASSWORD: "Passwort",
    LOGIN: "Anmelden"
  }
}

Authentication Module Mockup

Just for testing purposes. You can login with the username "admin" and the password "admin".

Authentication Module Loopback

Authentication module which works with Loopback as authentication backend in combination with the package @codecoupler/cc-api-auth.

Will be instatiated with new AuthModLoopback(options). Accepts the following options object:

1
2
3
4
5
6
7
8
9
{
  controller: {
    base: "/",        //Base path to the API
    login: "login",   //Controller name for "login"
    logout: "logout", //Controller name for "logout"
    me: "me"          //Controller name for "me"
  },
  messages: {}        //Translate messages (see below)
}

In case of an error Loopback sends an status code and a message. This message will be shown in the authentication application. You can map this messages by status code:

1
2
3
4
5
{
  messages: {
    500: "Set new message for status 500 here"
  }
}