I think here are two starting points.
Web services/REST services on the same server as your single web app.
It's not uncommon to do so nor it is necessarily bad, and also clearly the easiest way. Whatever authentication you choose (basic, digest, form, etc.) the API and the app are going to share the same user sessions on the server. Which means several things:
- The API can easily detect whether the user is logged or not, and obtain its permissions, roles or whatever you need to know if the user is allowed to perform a given action.
- You can deploy both the API and the app at the same time.
- Scaling the app is simple, just deploy both the API and the app to another instance. Load balancing is also easier.
- Monitoring the app is simple.
- On the negative side, if you want another app to use this API you'll have to either store this new app on the same server, or negotiate the user login yourself (for example, transmitting the credentials to the server). It may be even more complicated if your new app is NOT a web app.
I think this approach is perfectly suited for quickly launching simple websites and/or for API that will not need to be accessed by other services. And even if you start with that approach (we know everything is possible when it comes to programming, right?) you can still at one point switch to the split API/app model.
Web services/REST services on one server and the app on another.
The best practice today for authenticating users to a SPA linked to an "external" API, or an API stored on a different instance of the server is to imitate OAuth in its principles. The key is to use ... keys, or tokens obtained from your main server. The basic flow should be:
- Authenticate on the main server (where the app is hosted)
- If the authentication is successful, create a token and send it in the HTTP response, for example in a cookie.
- Whenever you need to access your API, send the token. It may be sent in a custom HTTP request header for example.
- The API receives the request and checks :
- If the requested resource needs a token, otherwise it's ok (example: returning information available to guests, unauthenticated users)
- If the token is there, check if it's still acceptable. Deciding how and when the token expires is up to you, there are various ways to design the token expiration strategy.
- Return the response data (optionally, with an updated token)
This approach allows you to:
- Dissociate the API from the apps that are going to use it AND from the user system in itself. Granted, you have to somehow interpret the token, but you don't need to know how and where the user data is stored. If you want a new web app or client/server app to access this API, all you need is a proper token.
- Deploy separately. For example, updating or fixing your API will not cause the single app server to go down.
- Monitor usage of your API only.
To conclude, apply the KISS and YAGNI principles before deciding. And remember you can always go from one model to another.