Now that our Forge APIs are available it's time to play with them! But after the initial euphoria is behind, you should think about implementing secure applications in order to protect your resources but also the ones of customers logging to your web application. This starts by getting your OAuth authentication workflow right.
Before we start I need to mention that I'm far from being a security expert, the information exposed in that blog post is purely extracted from my own experience and the advice I could collect when writing my samples, this should be considered in no way as a recommended approach but rather a suggestion. With that being said, let's get started!
Our web APIs are using OAuth as authentication mechanism, nothing really fancy here. There are two types of OAuth authentication contexts:
- 2-legged: also called "app-context" where your application (a server, desktop or mobile app) directly request an access token from the Autodesk server and can invoke the APIs with no user intervention. This is the case for the OSS API. Your app can for example create a bucket and start uploading some design there
- 3-legged: also called "user-context" where your app will request explicitly through a web interface (could be a WebView on a mobile or desktop app) a set of permissions from a user in order to access and potentially manipulate his data
Based on the kind of features you want to provide, you may want one or the other, or both, potentially mixing each approaches. There are however some subtleties in the way you have to request tokens and especially expose that to your client application.
I 2-Legged Token
Let's start with 2-legged because it's more straightforward: here your app has access to clientId and clientSecret credentials - requested through our dev portal - which have to remain protected on your server or secured within the App - understand some kind of encryption and/or some custom obfuscation. You can share the clientId but may the clientSecret be compromised, another developer would have full access to your data. In such a case you can regenerate a secret on our API portal. Your app will use those credentials to issue a valid token, you also have to pass the scope which can be a single value or a space-separated, url-encoded string in case you request multiple scopes, eg data:read data:write (see my method requestToken in the sample below). Once a token is acquired, you may use it on your server to access the API or expose your own API endpoints for your client app, so a user will be able for example to upload his file to a bucket. In that case it's important that this data:write token remains hidden on your server because otherwise you expose the possibility to a bad intentioned user to mess up with all your data.
If you want to let a user load his model in the viewer on a web page, you need to expose an endpoint that returns a data:read token, so in that case you can request a new token with only data:read scope and expose that as an endpoint, in the case of my sample this is /api/forge/token/2legged
A 2-legged token expires after a certain time - expires_in field of the response - you can either keep refreshing the token and always keep a valid token on your server, or request a fresh token for each new operation. Alternatively implement a retry mechanism that will request a token only if the current one is expired at at the time of the call - if you get an 'Unauthorized' error basically. All approaches are valid and depend on the kind of feature you are providing and how often your app is going to require tokens I would say.
II 3-legged Token
3-legged is a bit more tricky because here issuing a valid token depends on user authentication so at some point you need to prompt the user for his Autodesk credentials in conjunction of the API keys that you have stored on the server. You also need to specify scopes and this will explicitly be shown to user on the login page so he knows the extends of the permissions granted to your app.
A 3-legged token also has an expiry period, but unlike the 2-legged workflow where you can simply request a new token, in 3-legged you would need to request user credentials again to issue a brand new token. For that reason each token response contains a refresh_token field that is a code you can use with a specific endpoint to request a new valid 3-legged token without user interaction. For that reason it is very important that you keep that refresh code securely on your server and do not share it with the users or expose it in any way to the outside.
Similarly to a 2-legged token, in case you want to use the viewer to load user models, you need to expose an endpoint that would let you client application obtain a data:read token, without exposing a full-privilege token. You do not want to ask a user to log twice in order to obtain a second read-only token, so in that case you need to use the refresh_token field of the initial response in order to request what we can call a "downgraded token" that's a token that is requested from the refresh code but with a reduced set of scopes. The reduced scopes can only be a subset of the initial token scopes. The downgraded token response will also contain a new refresh_token code, you need to carefully store that because each refresh code can only be used once. You need to use that refresh code also to refresh the full-privilege token.
Below is the implementation of my Forge sample relevant to OAuth workflow and tokens management. I am splitting the code into the implementation of the endpoints and a utility called ForgeSvc (Forge Service) which allows some decoupling and flexibility in case the service has to be used by other endpoints or other services within the App. I find the approach pretty convenient even if it requires more files and more code than direct implementation in the endpoints. It definitely scales better once the codebase grows and the app becomes more complex.
Here is the OAuth workflow and secure forge token endpoints:
And here is the implementation of the Forge Service which keeps tracks of 3-legged and 2-legged tokens. It worth mentioning that it is NOT a good idea or secure approach to store tokens into the session properties (in your node.js code for example: req.session.token = 'xxxxxxx') because this can be hacked easily. At the moment my service is using a simple in-memory storage which can be replaced by a more sophisticated persistence mechanism later on without breaking the endpoints:
The complete source code of that sample is available there but keep in mind that, like our Forge API's, it is a work in progress... A live sample is available at https://forge.autodesk.io
Finally for more info and step-by-step tutorials on Forge OAuth, please refer to the official documentation.