GitHub Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

General provider information

Because Uitsmijter does not store any user data to authenticate a login, request providers are written to check if given credentials are valid. Each tenant has a set of providers to do certain tasks. The User Login Provider is responsible for the user backend which knows how to verify user credentials. The User Validation Provider is responsible to check if a username still exists in the backend user store.

The design of the authorization server is built to work with different - especially already existing - backends. It is one of the goals to easily be attachable to existing projects. Replacing an old in-app login form with an SSO should be very simple and straight forward. Tasks like user migration is not necessary as user data stays in its existing location. Changing user backends means changing the provider code and not migrating user data.

Language

Provides are written in ECMA-Script (better known as JavaScript). The runtime is based upon Webkit and has a few additional convenience functions:

Function Description
console.log Logs to the info level
console.error Logs to the error level
say A shorthand for console.log
fetch Method to fetch an external resource
commit Commit the provider’s result and hand back to the process
md5 Hashes a string into a md5 checksum
sha256 Hashes a string into a sha256 checksum

Provider classes to implement

Each tenant has to implement a User Login Provider code snippet and a User Validation Provider (both are called: provider)

Minimal example:

  providers:
    - class UserLoginProvider {
      constructor(credentials) { commit(true); }
      get canLogin() { return true; }
      get userProfile() { return {message:"DO NOT USE THIS IN PRODUCTION"}; }
      get role() { return "development"; }
      }
    - class UserValidationProvider {
      constructor(args) { commit(true); }
      get isValid() { return true }
      }

The providers are responsible for verifying the user and getting the profile of a user into the authorization server. Providers are only glue code and normally should not implement any business logic at all. Usually, providers are sending a request to some service and committing the result back.

Example:

  providers:
    - |
      class UserLoginProvider {
        isLoggedIn = false;
        profile = {};
        role = null;
        constructor(credentials) {
          fetch(`http://checkcredentials.checkcredentials.svc.cluster.local/validate-login`, {
            method: "post",
            body: { username: credentials.username, passwordHash: sha256(credentials.password) }
          }).then((result) => {
            var subject = {};
            profile = JSON.parse(result.body);
            if (result.code == 200) {
              this.isLoggedIn = true;
              this.role = profile.role;
              subject = {subject: profile.userId};
            }
            commit(result.code, subject);
          });
        }
        get canLogin() { return this.isLoggedIn; }
        get userProfile() { return this.profile; }
        get role() { return this.role; }
      }      
    - |
      class UserValidationProvider {
        isValid = false;
        constructor(args) {
          fetch(`http://checkcredentials.usertrunk.svc.cluster.local/validate-user`, {
            method: "post",
            body: {
              username: args.username,
            }
          }).then((result) => {
            response = JSON.parse(result.body);
            if (result.code == 200 && response.isDeleted === false) {
              this.isValid = true;
            }
            commit(this.isValid);
          });
        }
        get isValid() {
          return this.isValid;
        }
      }      

For the UserLoginProvider you have to commit the results within the constructor method. You also have to provide the three getters:

  • canLogin
  • userProfile
  • role

For the UserValidationProvider you have to commit a status within the constructor method that indicates that your operation is done. You also have to provide one getter:

  • isValid

The execution time of a provider is limited. The advanced setting SCRIPT_TIMEOUT can manipulate that behaviour in the future, but the default (and this is what you should use, if not less) is set to 30 seconds. The provider has to complete all tasks within this time limit, this includes performing all necessary requests and reply with the result.

Further readings