Pergunta

I am programming an application and am having trouble finding a good architecture for some of its components.

As part of the front-end app, I have a user authentication system and an API that allows users to perform certain actions on a server.

I have built the following components: (This is in javascript, but I imagine the architecture is not very language specific)

A component handling authentication that is stateful (it manages who is logged in and handles storing and caching their authtokens)

const Auth = {
    logout() { ... }
    login() { ... }
    isLoggedIn() { ... }
    getUsername() { ... }   
}

And a component handling API calls.

const API = {
    updateUserSettings() { ... }
    getPublicNotifications() { ... }
    sendEmail() { ... }
}

The issues is that these two components are very tightly coupled and have a circular dependancy. You see, for Auth to login it must use API to make a request to the server.

const Auth = {
    login(email, password) {
        ...
        this.authToken = API.getAuthToken(email, password);
        ...
    }
}

But for certain API requests (not all), the API object needs to retrieve authentication data:

const API = {
    updateUserSettings(settings) {
        ...
        this.request({ ...settings, authToken: Auth.getAuthToken() });
        ...
    }
}

I cannot even separate these two distinct components into their own files, as when they import each other they cause an endless loop due to their circular relationship.

How can I better approach this problem? I have considered making a third object that wraps both, but I am afraid of building a super object responsible for too many things. There are part of the program that interact only with Auth or only with the API, making their separation intuitive.

I have also considered splitting API into the parts that touch the initial authentication procedure, and methods that already assume authentication. But these two share a lot in common so breaking API up like that seemed like an imperfect approach. (But perhaps I'm wrong in thinking so)

Any help or ideas would be greatly appreciated. How would you handle this?

Foi útil?

Solução

There are two primary ways that I like for handling this on a single page javascript application and/or mobile application.

  • Basic Auth. You authenticate every request with user credentials and therefore don't need an explicit login. All requests should be encrypted with HTTPs to help keep credentials secure.

On a single page application, what is login? It's really your application thinking that it has valid authentication credentials. So when the user tries to login you can simulate this by calling something like getUsername or something similar with credentials that you think are right. A nice side effect of this is that you don't need to call both Login & some other request if the login works you can skip straight to asking for whatever data you want.

In practice that would be a single Api and therefore is easy to implement.

const UserApi = {
    checkLogin() { ... }
    getUsername() { ... }   
    updateUserSettings() { ... }
    getPublicNotifications() { ... }
    sendEmail() { ... }
}

This does not sound like what you want, and from a security perspective, it's not preferred since you have to keep the user's real credentials on hand (or hashed credentials) for anytime you want to make a request.

  • Token based Auth. Basically just a separate area of your application just for retrieving a token. Once you have that token you use it everywhere else for all requests that require authentication. There are a bunch of schemes for how to do token based authentication.

It might wind up something like this

const TokenApi = {
    getToken() { ... }
    deleteToken() { ... }
}

const UserApi = {
    updateUserSettings(settings) {
        ...
        this.request({ ...settings, authToken: Auth.getAuthToken() });
        ...
    }
    getPublicNotifications() { ... }
    sendEmail() { ... }
    getUsername() { ... }
    logout() { ... Auth.deleteToken() }
}

Some benefits of using tokens: https://stackoverflow.com/a/27119226/1318813

There are a bunch of ways of doing token based authentication. The simplest is just to keep a random string, maybe a hash, something unique to the user that the user didn't provide in your database that represents the user. This has some disadvantages mostly if it never changes, but even this is a huge step up from reusing the user's password.

A conventional best practice example of Token based authentication is OAuth 2.

In general, when building a new application I think OAuth 2 is a little too bulky to include when starting out. And besides, as long as you separate the concerns of authentication from the rest of your application you won't have any trouble going back and replacing a lame token or basic authentication with something more secure in a few weeks. It's almost never the first thing I build.

There are some other concepts that you might want to learn about as well. These take advantage of some browser & server features.

  • Session Ids. The most common example of this I know of is PHP Sessions.
  • Cookies
  • And... even more: Federated logins, single sign-on, NTLM, Kerberos.
Licenciado em: CC-BY-SA com atribuição
scroll top