Setting up ERPNext as oauth2 provider

Hi,

I’m trying to setup our ERPNext instance as an oauth2 provider but didn’t get it working so far. My scenario is that I have an ecommerce backend that needs to auth on the ERP and read/write documents.

When I issue a oauth2 request on the server, it tells me to enter a Social Login Key. Then, if I go to the ERP to do that, it seems to require a Frappe server. Now I’m confused: can erpnext act as an IDP/oauth2 provider by itself? If so, how do you setup that? I’ve read all the docs and forum posts I could find without any luck.

Thanks!

Guillaume

1 Like

The error will be
Define Frappe Server URL in Social Login Keys

No need to add social login key on the IDP only add the frappe server url on IDP.
IDP must have OAuth Client Added. keep client secret and client id blank on idp.

On other frappe server which connect to this frappe IDP, add server url as well as client secret and client id from OAuth 2 client added on IDP.

If you are building single signon for your own website. no need to add client id and secret here at all. Only Frappe server url is enough

Yes frappe can act as IDP. As an example Android Authenticator uses Frappe as IDP. There are other apps as well.

Also OAuth Bearer Token can be reused in various ways. e.g. Custom SMS OTP or QR code based 2FA

Android Authenticator:


Forum Posts:

1 Like

Thanks @revant_one for these information!
While I get the idea, it’s still not clear for me how to setup a Frappe server as IDP so I can fill in the “Frappe Server” field in the Social Login Keys form of my ERPnext instance. Could you provide me a bit of guidance with that? Thanks :slight_smile:

Btw, your android app looks awesome, can’t wait to try this, thanks!

Guillaume

Also, if it can help, here’s the message returned by my oauth2 client:

2017-05-24 20:36:58 mouette requests_oauthlib.oauth2_session[18171] DEBUG Response headers were {'Content-Type': 'application/json', 'Content-Length': '133', 'Date': 'Wed, 24 May 2017 18:36:59 GMT', 'Server': 'nginx/1.10.0 (Ubuntu)', 'Set-Cookie': 'user_image=; Path=/, user_id=Guest; Path=/, system_user=yes; Path=/, full_name=Guest; Path=/, sid=Guest; Expires=Sat, 27-May-2017 20:36:59 GMT; Path=/', 'Connection': 'keep-alive'} and content {"_server_messages":"[\"{\\\"message\\\": \\\"Define Frappe Server URL in Social Login Keys\\\", \\\"indicator\\\": \\\"red\\\"}\"]"}.
2017-05-24 20:36:58 mouette requests_oauthlib.oauth2_session[18171] DEBUG Invoking 0 token response hooks.

File "/home/glibersat/Sources/lib/python3.5/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 386, in validate_token_parameters
    raise MissingTokenError(description="Missing access token parameter.")
oauthlib.oauth2.rfc6749.errors.MissingTokenError: (missing_token) Missing access token parameter.

The message returned by oauth2 client suggests

  1. Frappe Server URL is missing.
  2. oauth2 client assumes that you’ve access_token and proceeds with the request resulting into missing access token

The guide about setting up IDP is for a scenario where both IDP and Client App are two separate frappe servers.

In your case the Client App is not a frappe server. So only Frappe Server you’ve setup must have Frappe Server URL setup. No need for Client Secret and Client ID

Example for Frappe Server URL setup for IDP/Primary frappe server
erp.mntechnique.com is the Frappe IDP and the main Frappe Server with ID related resource.

same server hosts the OAuth Clients.

rauth is python oauth client library used in Frappe. Example for using rauth

The same thread has more gui friendly postman example

OAuth Client Libraries are available.

Broad points for developing apps using IDs on Frappe server. (with or without libraries)

  1. Configure Frappe Server URL and Add OAuth Client on Frappe IDP
  2. In Client App: check for previously stored bearer token (json) (store token only in trusted app, storing token is not secure)
  3. In Client App: Use access_token from stored bearer token on openid_profile endpoint and check response.
  4. In Client App: If response is valid username, proceed with using the access_token in header.
  5. In Client App: If response is invalid, request get_token endpoint with refresh_token stored in old bearer token to generate a new bearer token (also replace old bearer token) and access the resource with this new access_token
  6. In Client App: If response is invalid for refresh_token as well, request authorize endpoint. Generally “Login with Frappe” button or anywhere seamless in app workflow. This url resolution initiates the grant flow and user must login (if not already logged in) and allow access to resource (skip_authorization can be configured in oauth client on IDP). Successful grant results in Auth Code which is communicated on redirect_uri
  7. In Client App: when browser resolves this uri along with authcode, REST endpoint on client app gets the code and requests get_token for new bearer token (also replace old bearer token) using this code.
  8. In Client App: In case of untrusted mobile app use implicit grant https://tools.ietf.org/html/rfc6749#section-4.2 which does not generate any refresh token and user has to login every time.
  9. In Client App: In case of trusted mobile app use Authcode/bearertoken grant https://tools.ietf.org/html/rfc6749#section-4.1 just use such redirect uri which has to be read on mobile. check android example how it uses localhost.

8 Likes

This is tremendously helpful.

One problem I am running into while trying to integrate an external application: when making a request with an expired token, I get a 403 error that seems to be identical to the error I get when making a request without sufficient system permissions. Likewise, in my application’s api layer, it’s not clear whether to notify the user that they don’t have access or to submit a refresh_token grant request.

Any thoughts about the best design here?

If the refresh_token logic is tied up with expiration time instead of 403 then permission error can be relayed to user.

Thanks @revant_one, I’ll give that a shot!