OAuth 2
Use the header Authorization: Bearer
to perform authenticated requests. You can receive a bearer token by combining the following two requests.
Here is an amazing introduction to OAuth: OAuth 2.0 and OpenID Connect (in plain English)
GET /api/method/frappe.integrations.oauth2.authorize
Get an authorization code from the user to access ERPNext on his behalf.
Params (in query):
client_id (string)
ID of your OAuth2 application
state (string)
Arbitrary value used by your client application to maintain state between the request and callback. The authorization server includes this value when redirecting the user-agent back to the client. The parameter should be used for preventing cross-site request forgery.
response_type (string)
"code"
scope (string)
The scope of access that should be granted to your application.
redirect_uri (string)
Callback URI that the user will be redirected to, after the application is authorized. The authorization code can then be extracted from the params.
code_challenge_method (string)
(OPTIONAL) Can be one from
s256
orplain
.code_challenge (string)
(OPTIONAL) Can be
base64encode(sha256(random_string))
in casecode_challenge_method=s256
orrandom_string
in casecode_challenge_method=plain
. Refer https://tools.ietf.org/html/rfc7636#appendix-A
Example:
curl -X POST https://{your frappe instance}/api/method/frappe.integrations.oauth2.authorize \
--data-urlencode 'client_id=511cb2ac2d' \
--data-urlencode 'state=444' \
# base64encode(sha256('420')) => 21XaP8MJjpxCMRxgEzBP82sZ73PRLqkyBUta1R309J0
# --data-urlencode 'code_verifier=21XaP8MJjpxCMRxgEzBP82sZ73PRLqkyBUta1R309J0' \
--data-urlencode 'response_type=code'
--data-urlencode 'scope=openid%20all' \
--data-urlencode 'redirect_uri=https://app.getpostman.com/oauth2/callback'
Returns:
- HTTP Code: 200
text/html
This will open the authorize page which then redirects you to the
redirect_uri
.
If the user clicks 'Allow', the redirect URI will be called with an authorization code in the query parameters:
https://{redirect uri}?code=plkj2mqDLwaLJAgDBAkyR1W8Co08Ud&state=444
If user clicks 'Deny' you will receive an error:
https://{redirect uri}?error=access_denied
Token Exchange for Authorization Code Grant with ID Token
POST /api/method/frappe.integrations.oauth2.get_token
Header Content-Type: application/x-www-form-urlencoded
Note: This endpoint can also be used to get a refreshed access token. Just send the
refresh_token
in the request body.
Trade the authorization code (obtained above) for an access token.
Params (in body):
grant_type (string)
"authorization_code"
code (string)
Authorization code received in redirect URI.
client_id (string)
ID of your OAuth2 application
redirect_uri (string)
Registered redirect URI of client app
Example:
curl -X POST https://{your frappe instance}/api/method/frappe.integrations.oauth2.get_token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json' \
-d 'grant_type=authorization_code&code=wa1YuQMff2ZXEAu2ZBHLpJRQXcGZdr
&redirect_uri=https%3A%2F%2Fapp.getpostman.com%2Foauth2%2Fcallback&client_id=af615c2d3a'
For testing purposes you can also pass the parameters in the URL like this (and open it in the browser):
https://{your frappe instance}/api/method/frappe.integrations.oauth2.get_token?grant_type=authorization_code&code=A1KBRoYAN1uxrLAcdGLmvPKsRQLvzj&client_id=511cb2ac2d&redirect_uri=https%3A%2F%2Fapp.getpostman.com%2Foauth2%2Fcallback
Returns:
{
"access_token": "pNO2DpTMHTcFHYUXwzs74k6idQBmnI",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "cp74cxbbDgaxFuUZ8Usc7egYlhKbH1",
"scope": "openid all",
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o"
}
Token Exchange for Authorization Code Grant with ID Token (PKCE)
POST /api/method/frappe.integrations.oauth2.get_token
Header Content-Type: application/x-www-form-urlencoded
Trade the authorization code (obtained above) for an access token.
Params (in body):
grant_type (string)
"authorization_code"
code (string)
Authorization code received in redirect URI.
client_id (string)
ID of your OAuth2 application
redirect_uri (string)
Registered redirect URI of client app
code_verifier (string)
random_string
used duringAuthorization Request
withcode_challenge_method
andcode_challenge
.
Content-Type: application/x-www-form-urlencoded
Example:
curl -X POST https://{your frappe instance}/api/method/frappe.integrations.oauth2.get_token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json' \
-d 'grant_type=authorization_code&code=wa1YuQMff2ZXEAu2ZBHLpJRQXcGZdr
&redirect_uri=https%3A%2F%2Fapp.getpostman.com%2Foauth2%2Fcallback&client_id=af615c2d3a&code_verifier=420'
For testing purposes you can also pass the parameters in the URL like this (and open it in the browser):
https://{your frappe instance}/api/method/frappe.integrations.oauth2.get_token?grant_type=authorization_code&code=A1KBRoYAN1uxrLAcdGLmvPKsRQLvzj&client_id=511cb2ac2d&redirect_uri=https%3A%2F%2Fapp.getpostman.com%2Foauth2%2Fcallback&code_verifier=420
Returns:
{
"access_token": "pNO2DpTMHTcFHYUXwzs74k6idQBmnI",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "cp74cxbbDgaxFuUZ8Usc7egYlhKbH1",
"scope": "openid all",
"id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o"
}
Revoke Token Endpoint
POST /api/method/frappe.integrations.oauth2.revoke_token
Header: Content-Type: application/x-www-form-urlencoded
Revoke token endpoint.
Params:
token
Access token to be revoked.
Returns:
Always returns empty response with HTTP status code 200.
{}
Open ID Connect id_token
ID Token is a JWT.
aud
claim has registeredclient_id
.iss
claim has frappe server url.sub
claim has Frappe User's userid.roles
claim has user roles.exp
claim has expiration time.iat
claim has issued at time.
Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkouIERvZSIsImVtYWlsIjoiakBkb2UuY29tIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDIwMjIsImF1ZCI6ImNsaWVudF9pZCJ9.ZEdnrHjLbArahVTN19b4zoRFoBO5a2BakRkR82O1VU8
Verify and extract it with PyJWT.
import jwt
id_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkouIERvZSIsImVtYWlsIjoiakBkb2UuY29tIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTYyNDIwMjIsImF1ZCI6ImNsaWVudF9pZCIsInJvbGVzIjpbIlN5c3RlbSBNYW5hZ2VyIiwiU2FsZXMgTWFuYWdlciJdLCJub25jZSI6Ijc4OTEyMyIsImlzcyI6Imh0dHBzOi8vZXJwLmV4YW1wbGUuY29tIn0.F8Wbh5dtD1loZPltJLj_sqF9DZNeBvEbo-ITtf3UPqk"
client_id = 'client_id'
client_secret = 'client_secret'
payload = jwt.decode(
id_token,
audience=client_id,
key=client_secret,
algorithm="HS256",
options={"verify_exp": False}, # Enabled by default to verify expiration time
)
print(payload)
# Output
{'sub': '1234567890', 'name': 'J. Doe', 'email': 'j@doe.com', 'iat': 1516239022, 'exp': 1516242022, 'aud': 'client_id', 'roles': ['System Manager', 'Sales Manager'], 'nonce': '789123', 'iss': 'https://erp.example.com'}
OpenID User Info Endpoint
Request
GET /api/method/frappe.integrations.oauth2.openid_profile
Header: Authorization: Bearer valid_access_token
Response
{
"sub": "1234567890",
"name": "J. Doe",
"given_name": "J",
"family_name": "Doe",
"iss": "https://erp.example.com",
"picture": "https://erp.example.com/files/jdoe.jpg",
"email": "j@doe.com",
"iat": 1516239022,
"exp": 1516242022,
"aud": "client_id",
"roles": ["System Manager", "Sales Manager"]
}
Introspect Token Endpoint
POST /api/method/frappe.integrations.oauth2.introspect_token
Header: Content-Type: application/x-www-form-urlencoded
Revoke token endpoint.
Params:
token_type_hint
access_token
orrefresh_token
, defaults toaccess_token
if nothing is providedtoken
Access token or Refresh Token to be introspected. Depends on token_type_hint
Returns:
{
"client_id": "511cb2ac2d",
"trusted_client": 1,
"active": true,
"exp": 1619523326,
"scope": "openid all",
"sub": "1234567890",
"name": "J. Doe",
"given_name": "J",
"family_name": "Doe",
"iss": "https://erp.example.com",
"picture": "https://erp.example.com/files/jdoe.jpg",
"email": "j@doe.com",
"iat": 1516239022,
"exp": 1516242022,
"aud": "511cb2ac2d",
"roles": ["System Manager", "Sales Manager"]
}
OR
{
"active": false,
"_server_messages": "..."
}
Further Reading
Please check Guides / Integration / How To Set Up Oauth
to see how to create a new OAuth 2 client.
Authors:
- Raffael Meyer (raffael@alyf.de)
- Revant Nandgaonkar (revant@castlecraft.in)