OAuth 2
Use the header
Authorization: Bearer <access_token>
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 provided -
token
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)
</access_token>