Prerequisites

By now, we assume you've followed the steps in overview and have a very basic admin running under (for the sake of this tutorial) /admin/. If you haven't, go there and do that first.

REST API

Another important thing your application will need is an API. Exactly how that should work is outside the scope of this tutorial; Monad is a client side framework and by design doesn't care about your server.

Monad is built upon the premise that most applications will offer a REST API of some sort. E.g. if you use the popular Worpress system (we personally don't like it, but hey - it's popular) you'll be able to install the Wordpress API plugin to expose your data.

In brief (and as a gross oversimplification) you can think of the API as a bridge between your browser and your application's database, where GET, POST and DELETE HTTP Ajax calls represent SELECT, INSERT/UPDATE and DELETE operations respectively. APIs often follow this abstract pattern:

GET/POST/DELETE /some/base/url/{TABLE-OR-MODEL}/{OPTIONAL-ID} data:field=value

A "proper" API will do more than SELECT * FROM {TABLE}, e.g. when getting a "post" from the Wordpress API you'll also retrieve related images etc. But this is implementation-specific.

Quick and dirty for PHP users

If your site's built on PHP but doesn't use some framework already supplying an API out of the box, you might want to check out our Monki API bootstrapper. It's a Composer package to easily expose (parts of) your application's database via a simple "default API", including access control. It should at least get you up and running quickly, and for admin purposes usually offers enough functionality (if you're building a public facing API as well, you'll probably need to manually write out all the calls anyway).

Authentication

Since Monad is a client-side framework, we'll need some way of authenticating a user to see if she has access to our admin. We don't make any assumptions regarding your authentication scheme, so this is something you'll have to configure and implement yourself. Even Monki won't do it for you...

The monadLogin component

This component was previously called moLogin. The old name is still supported via an alias, but is deprecated and will be removed in a future version of Monad.

Sections requiring an access check should be wrapped in the <monad-login> component. Its default behaviour is to show the transcluded content if access is granted, and otherwise show its own template (which should contain something useful, like a login form or a notification).

monadLogin optionally takes an auth parameter. If undefined it defaults to a service called Authentication. You will need to write this since Monad cannot know how you determine if a user has enough access rights.

Important: Your API should of course do its own authentication checks and not solely rely on the client side hiding stuff from users.

The default behaviour when unauthenticated is to show a form with username and password fields. If your admin requires a different authentication method (e.g. two tier using text messages), you should override the template. Since all Monad templates are baked into the Javascript this is very simple:

angular.module('myModuleName').run(['$templateCache', $templateCache => {
    $templateCache.put('Monad/components/Login/template.html', `
<div ng-transclude ng-if="$ctrl.check"></div>
<!-- your custom login form here -->`);
}]);

The only important thing is to remember to include the ngTransclude directive. If you omit it, authenticated user won't see anything.

Interface

Monad expects at least a required service to be registered in Angular as Authentication. So, let's set up our own implementation. The service needs to implement the following "interface":

Javascript doesn't support interfaces, so this is more of a reference example providing dummy methods. You must override this! Monad's source is in ES6, but this is transpiled to ES5 using Babel anyway so you're free to write all your own code directly in "old skool" ES5.

export default class AuthenticationService {

    ['status']() {
        // Return a promise reading the current authentication status.
    }

    attempt(credentials) {
        // Returns a promise attempting authentication using supplied
        // `credentials` (a key/value hash). What `credentials` you pass depends
        // on your application's needs - a common scenario is of course
        // `username` and `password`, but if you also require `mobile_number`,
        // `social_security` and `mothers_maiden_name` that's all fine too.
    }

    revoke() {
        // Returns a promise attempting to revoke the current authentication
        // status (i.e., "logout").
    }

    get check() {
        // Return true if the user is authenticated according to the
        // current session, false otherwise. If your login scheme involves
        // stuff like roles, this should check for the correct ones
        // accordingly.
    }

}

If you're writing in ES6 yourself, it's a good idea to import and extend this default implementation, so you'll quickly notice when you forget to define and methods. In any case, register your custom implementation as a service on your admin's main module:


import Authentication from 'monad-cms/services/Authentication';

class myAuthentication extends Authentication {
\   // Your custom implementation
};

angular.module('awesome', ['monad-cms'])
    .service('Authentication', myAuthentication);

Note that this is now the global authentication for your entire application. In other words, if you need to support "roles" or such, you would need multiple authentication services, e.g. AuthenticateAuthor, AuthenticateCommenter etc. Each authentication service can be injected into a monadLogin directive via the auth attribute like so:

<!-- Assuming customAuthentcation was placed on the $rootScope: -->
<monad-login auth="$root.customAuthentication"></monad-login>

Of course, authentication services can (and maybe should) in turn also inherit from the base authentication service - the assumption being you'll usually only need to override the check getter.

Generally you'll inject Angular's $http service to somehow talk to your backend, e.g. GET /api/session. But you could also use other schemes, e.g. websockets or authentication via a browser plugin. Anything is possible as long as you can express it in Javascript!

Again (since we can't stress this enough): never solely rely on client side authentication and make sure your API double-checks permissions server side as well!

An example custom implementation can be found in the advanced section.