Login Form

All CUBA REST API methods require an OAuth token. In order to obtain this token the client must authenticate using user’s login and password. This mechanism is described in the Developer’s Manual.

The cuba-login web component allows you to create a login form. It’s a simple UI component with 2 fields ("User Name", "Password") and the "Login" button. It sends two events on login attempt: cuba-login-success and cuba-login-error.

Below is a working example, please use login test and password test.

Source code

index.html
<html>
<head>
	<link rel="import" href="src/cuba/login/app-with-login.html">
	<script src="bower_components/webcomponentsjs/webcomponents-loader.js"></script>
</head>
<body>
    <app-with-login></app-with-login>
</body>
</html>
src/cuba/login/app-with-login.html
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../../bower_components/polymer/lib/elements/dom-if.html">
<link rel="import" href="../../../bower_components/cuba-app/cuba-app.html">
<link rel="import" href="../../../bower_components/cuba-app/cuba-app-aware-behavior.html">
<link rel="import" href="../../../bower_components/cuba-login/cuba-login.html">

<dom-module id="app-with-login">
  <template>

    <!-- 'cuba-app' always should be the first thing in an application -->
    <!--  uses CUBA REST API -->
    <cuba-app api-url="/app/rest/"></cuba-app>

    <template is="dom-if" if="[[!authenticated]]">
      <!-- Only after the user is authenticated -->
      <!--  the rest of the CUBA REST API can be used -->
      <cuba-login on-cuba-login-success="_onLoginSuccess" on-cuba-login-error="_onLoginError"></cuba-login>
    </template>

    <template is="dom-if" if="[[authenticated]]">
      <h3>Congratulations! You are authenticated and now can use cuba REST API!</h3>
      <div>
        <button on-click="_logout">Logout</button>
      </div>
    </template>

  </template>
  <script>
    class AppWithLogin extends Polymer.mixinBehaviors([CubaAppAwareBehavior], Polymer.Element) {
      static get is() {
        return 'app-with-login';
      }

      static get properties() {
        return {
          authenticated: {
            type: Boolean,
            value: false
          }
        };
      }

      _onLoginSuccess() {
        this.set('authenticated', true);
      }

      _logout() {
        this.set('authenticated', false);
        this.app.logout();
      }

      _onLoginError() {
        alert('Bad credentials');
      }

    }

    customElements.define(AppWithLogin.is, AppWithLogin);
  </script>
</dom-module>
Styling

If you check cuba-login source code, you can see that the component is opened for extension by using custom property mixins.

bower_components/cuba-login/cuba-login.html
  #form {
    @apply --cuba-login-form;
  }
  #username {
    @apply --cuba-login-username-input;
  }
  #password {
    @apply --cuba-login-password-input;
  }
  #submit {
    @apply --cuba-login-submit-button;
  }
  .actions {
    display: flex;
    flex-direction: row-reverse;
    @apply --cuba-login-actions;
  }
  ...
  <form id="form">
    <div class="fields">
      <paper-input type="text" id="username" label="[[msg('User Name')]]" value="{{username}}"></paper-input>
      <paper-input type="password" id="password" label="[[msg('Password')]]" value="{{password}}"></paper-input>
    </div>
    <div class="actions">
      <paper-button id="submit" on-tap="submit">[[msg('Login')]]</paper-button>
    </div>
  </form>

Below you can see how these mixins can be implemented. The example is the same as above but contains the <style/> section.

src/cuba/login/app-with-login-styled.html
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../../bower_components/polymer/lib/elements/dom-if.html">
<link rel="import" href="../../../bower_components/cuba-app/cuba-app.html">
<link rel="import" href="../../../bower_components/cuba-app/cuba-app-aware-behavior.html">
<link rel="import" href="../../../bower_components/cuba-login/cuba-login.html">

<dom-module id="app-with-login-styled">
  <template>
    <style>
      .login-form {
        /* That's how we define a mixin */
        --cuba-login-form: {
          display: flex;
          justify-content: center;
        };
        --cuba-login-actions: {
          padding: 5px;
        };
        --cuba-login-submit-button: {
          border: black solid 1px;
        };
      }
    </style>

    <cuba-app api-url="/app/rest/"></cuba-app>

    <template is="dom-if" if="[[!authenticated]]">
      <cuba-login class="login-form"
                  on-cuba-login-success="_onLoginSuccess"
                  on-cuba-login-error="_onLoginError"></cuba-login>
    </template>

    <template is="dom-if" if="[[authenticated]]">
      <h3>Congratulations! You are authenticated and now can use cuba REST API!</h3>
      <div>
        <button on-click="_logout">Logout</button>
      </div>
    </template>

  </template>
  <script>
    class AppWithLoginStyled extends Polymer.mixinBehaviors([CubaAppAwareBehavior], Polymer.Element) {
      static get is() {
        return 'app-with-login-styled';
      }

      static get properties() {
        return {
          authenticated: {
            type: Boolean,
            value: false
          }
        };
      }

      _onLoginSuccess() {
        this.set('authenticated', true);
      }

      _logout() {
        this.set('authenticated', false);
        this.app.logout();
      }

      _onLoginError() {
        alert('Bad credentials');
      }

    }

    customElements.define(AppWithLoginStyled.is, AppWithLoginStyled);
  </script>
</dom-module>

Result of styling

Writing your own login form

cuba-login, as any other CUBA component uses cuba-rest component API under the hood. It means that if you need some very custom login page, you can use the API directly. See an example below.

src/cuba/login/app-with-login-custom.html
<link rel="import" href="../../../bower_components/polymer/polymer-element.html">
<link rel="import" href="../../../bower_components/polymer/lib/elements/dom-if.html">
<link rel="import" href="../../../bower_components/cuba-app/cuba-app.html">
<link rel="import" href="../../../bower_components/cuba-app/cuba-app-aware-behavior.html">

<dom-module id="app-with-login-custom">
  <template>

    <cuba-app api-url="/app/rest/"></cuba-app>

    <template is="dom-if" if="[[!authenticated]]">

      <label>
        Please choose your login:
      </label>
      <br/>
      <br/>
      <select id="typeSelect">
        <option value="marketer">Marketer</option>
        <option value="manager">Manager</option>
        <option value="admin">Admin</option>
      </select>
      <br/>
      <br/>
      <button on-click="_login">Login</button>
    </template>

    <template is="dom-if" if="[[authenticated]]">
      <h3>Congratulations! You are authenticated and now can use cuba REST API!</h3>
      <div>
        <button on-click="_logout">Logout</button>
      </div>
    </template>

  </template>
  <script>
    class AppWithLoginCustom extends Polymer.mixinBehaviors([CubaAppAwareBehavior], Polymer.Element) {
      static get is() {
        return 'app-with-login-custom';
      }

      static get properties() {
        return {
          authenticated: {
            type: Boolean,
            value: false
          }
        };
      }

      _login() {
        const login = this.shadowRoot.querySelector('#typeSelect').value;

        let password;

        // In accordance with the best security anti-patterns
        switch (login) {
          case 'marketer':
            password = 'marketer!';
            break;
          case 'manager':
            password = 'manager:-)';
            break;
          case 'admin':
            password = 'admin123';
            break;
        }

        this.app.login(login, password).then(function() {
          this.set('authenticated', true);
        }.bind(this));
      }

      _logout() {
        this.set('authenticated', false);
        this.app.logout();
      }

      _onLoginError() {
        alert('Bad credentials');
      }

    }

    customElements.define(AppWithLoginCustom.is, AppWithLoginCustom);
  </script>
</dom-module>

Custom login form

Token expiration

A Polymer application receives a token after authentication and then uses it with every request.

By default, a token is valid for 12 hours. After this period requests stop working and the user has to re-login. We recommend to increase the token expiration time and use the persistent token store to save tokens on server restart.

cuba-app sends the cuba-token-expired event that can be used to handle the expiration appropriately.