From 88096cc54d117e14f9287a75c38c14f97cf7aa92 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Thu, 17 Aug 2023 10:18:47 -0400 Subject: [PATCH] docs: Add more examples related to Auth. --- docs/concepts/rest_apis.rst | 16 +-- docs/references/auth_code_samples.rst | 167 ++++++++++++++++++++++++++ docs/references/index.rst | 1 + 3 files changed, 176 insertions(+), 8 deletions(-) create mode 100644 docs/references/auth_code_samples.rst diff --git a/docs/concepts/rest_apis.rst b/docs/concepts/rest_apis.rst index 37c8390e60..2b381f648e 100644 --- a/docs/concepts/rest_apis.rst +++ b/docs/concepts/rest_apis.rst @@ -12,21 +12,21 @@ user and then pass that to the server as a part of the request header. You can get a JWT one of two ways, one is to exchange the username and password for a user to get their JWT, and the other is to get a JWT associated with an -OAuth2 Application (the application is associated with your user)that allow you +OAuth2 Application (the application is associated with your user) that allows you to manipulate other users and system resources so long as the user associated with the OAuth2 application has the permissions to do so. -The best way to interact with the APIs is to get a JWT Token associated with a -user and then pass that to the server as a part of the request header. - -You can get a JWT one of two ways, one is to exchange the username and password -for a user to get their JWT, and the other is to get a JWT associated with an -OAuth2 Application (the application is associated with your user). - JWTs by default expire every hour so when they expire you'll have to get a new one before you can call the API again. .. seealso:: * :doc:`/how-tos/use_the_api` + + * :doc:`/references/auth_code_samples` + * `OAuth2, JWT and Mobile `_ + + * `Open edX Rest API Conventions ` + + diff --git a/docs/references/auth_code_samples.rst b/docs/references/auth_code_samples.rst new file mode 100644 index 0000000000..eb2ed61d2c --- /dev/null +++ b/docs/references/auth_code_samples.rst @@ -0,0 +1,167 @@ +Authentication Related Code Samples +################################### + +.. warning:: + + Access Tokens, Refresh Tokens and Client Secrets are generally considered + secret and should not live in your code. We print them here so that these + examples are useful but you should generally not expose any of these tokens + to systems or clients you don't trust. + +Get a JWT with a Username and Password +************************************** + +.. code-block:: + + import requests + from pprint import pprint + + token_request = requests.post( + f"http://localhost:8000/oauth2/access_token", + data={ + "client_id": "login-service-client-id", + "grant_type": "password", + "username": "test_user", + "password": "test_password", + "token_type": "JWT", + }, + ) + pprint(token_request.json()) + +.. code-block:: + :caption: Output + + {'access_token': 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiAibG1zLWtleSIsICJleHAiOiAxNjkyMjExNjM4LCAiZ3JhbnRfdHlwZSI6ICJwYXNzd29yZCIsICJpYXQiOiAxNjkyMjA4MDM4LCAiaXNzIjogImh0dHA6Ly9sb2NhbGhvc3Q6MTgwMDAvb2F1dGgyIiwgInByZWZlcnJlZF91c2VybmFtZSI6ICJmZWFuaWwiLCAic2NvcGVzIjogWyJyZWFkIiwgIndyaXRlIiwgImVtYWlsIiwgInByb2ZpbGUiXSwgInZlcnNpb24iOiAiMS4yLjAiLCAic3ViIjogIjVjMTBmNjZmMmQ2MzkwYjcwNjYyYzkxNGFhZTdlZjc5IiwgImZpbHRlcnMiOiBbInVzZXI6bWUiXSwgImlzX3Jlc3RyaWN0ZWQiOiBmYWxzZSwgImVtYWlsX3ZlcmlmaWVkIjogdHJ1ZSwgImVtYWlsIjogImZlYW5pbEBheGltLm9yZyIsICJuYW1lIjogIkZlYW5pbCBQYXRlbCIsICJmYW1pbHlfbmFtZSI6ICIiLCAiZ2l2ZW5fbmFtZSI6ICIiLCAiYWRtaW5pc3RyYXRvciI6IHRydWUsICJzdXBlcnVzZXIiOiB0cnVlfQ.iGFl7qsAUau0-40oq8Of0f72kguq2Hc_drijCnI2I-M', + 'expires_in': 3600, + 'refresh_token': 'm8iXhVlGABu52xFxVFj5rAz8xSjsRq', + 'scope': 'read write email profile', + 'token_type': 'JWT'} + +.. note:: The client type must be ``public`` for this to work. + +Get a JWT with a client_id and client_secret +******************************************** + +.. code-block:: + + import base64 + import requests + + from pprint import pprint + + client_id = "ukbclQB8aPh7hgsy8ifPXkPf7fRqgUq1w21f2YZa" + # Note this should actually be secert and probably not in your code but + # provided here in the example + client_secret = "xkN0BJ19q9Jk8UPUppEtC1xe4764c81ioFtlegvokbmnAC7CFCT5gG1Og5nnFmCNc3NHNhUwWWDRVcBfnLSZ4xAlEmSePzfkFtLE06cwR1MuSc0gx9LUEjRrTs3j2vgK" + + credential = f"{client_id}:{client_secret}" + encoded_credential = base64.b64encode(credential.encode("utf-8")).decode("utf-8") + + + headers = {"Authorization": f"Basic {encoded_credential}", "Cache-Control": "no-cache"} + data = {"grant_type": "client_credentials", "token_type": "jwt"} + + token_request = requests.post( + "http://localhost:8000/oauth2/access_token", headers=headers, data=data + ) + + pprint(token_request.json()) + +.. code-block:: + :caption: Output + + {'access_token': 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiAibG1zLWtleSIsICJleHAiOiAxNjkyMjExNjM4LCAiZ3JhbnRfdHlwZSI6ICJjbGllbnQtY3JlZGVudGlhbHMiLCAiaWF0IjogMTY5MjIwODAzOCwgImlzcyI6ICJodHRwOi8vbG9jYWxob3N0OjE4MDAwL29hdXRoMiIsICJwcmVmZXJyZWRfdXNlcm5hbWUiOiAiZmVhbmlsIiwgInNjb3BlcyI6IFsicmVhZCIsICJ3cml0ZSIsICJlbWFpbCIsICJwcm9maWxlIl0sICJ2ZXJzaW9uIjogIjEuMi4wIiwgInN1YiI6ICI1YzEwZjY2ZjJkNjM5MGI3MDY2MmM5MTRhYWU3ZWY3OSIsICJmaWx0ZXJzIjogW10sICJpc19yZXN0cmljdGVkIjogZmFsc2UsICJlbWFpbF92ZXJpZmllZCI6IHRydWUsICJlbWFpbCI6ICJmZWFuaWxAYXhpbS5vcmciLCAibmFtZSI6ICJGZWFuaWwgUGF0ZWwiLCAiZmFtaWx5X25hbWUiOiAiIiwgImdpdmVuX25hbWUiOiAiIiwgImFkbWluaXN0cmF0b3IiOiB0cnVlLCAic3VwZXJ1c2VyIjogdHJ1ZX0.CX1S0QGrWKEPOHC8kUzGcvW8Ky04RCA8vU8WJrZURSw', + 'expires_in': 3600, + 'scope': 'read write email profile', + 'token_type': 'JWT'} + +.. note:: When you get a JWT using ``client_credentials`` you don't get a + refresh token. You're just expected to make a new call with your client + credentials. + +Check to see if a JWT is Expired +******************************** + +.. code-block:: + + import jwt + + # See above examples for how to get a JWT token + jwt_token = token_request.json()['access_token'] + + try: + jwt.decode(jwt_token, "secret", audience="lms-key", algorithms=['HS256']) + except jwt.ExpiredSignatureError: + # Signature has expired + +Refresh a JWT Using a Refresh Token +*********************************** + +.. code-block:: + + import requests + + # See above examples for how to get a JWT token with a refresh token + refresh_token = token_request.json()['refresh_token'] + + refreshed_token_request = requests.post( + f"http://localhost:8000/oauth2/access_token", + data={ + "client_id": "login-service-client-id", + "grant_type": "refresh_token", + "refresh_token": token_request.json()['refresh_token'], + "token_type": "JWT", + }, + ) + + pprint(refreshed_token_request.json()) + +.. code-block:: + :caption: Output + + + {'access_token': 'eyJhbGciOiJIUzI1NiJ9.eyJhdWQiOiAibG1zLWtleSIsICJleHAiOiAxNjkyMjE1MTgwLCAiZ3JhbnRfdHlwZSI6ICJwYXNzd29yZCIsICJpYXQiOiAxNjkyMjExNTgwLCAiaXNzIjogImh0dHA6Ly9sb2NhbGhvc3Q6MTgwMDAvb2F1dGgyIiwgInByZWZlcnJlZF91c2VybmFtZSI6ICJmZWFuaWwiLCAic2NvcGVzIjogWyJyZWFkIiwgIndyaXRlIiwgImVtYWlsIiwgInByb2ZpbGUiXSwgInZlcnNpb24iOiAiMS4yLjAiLCAic3ViIjogIjVjMTBmNjZmMmQ2MzkwYjcwNjYyYzkxNGFhZTdlZjc5IiwgImZpbHRlcnMiOiBbInVzZXI6bWUiXSwgImlzX3Jlc3RyaWN0ZWQiOiBmYWxzZSwgImVtYWlsX3ZlcmlmaWVkIjogdHJ1ZSwgImVtYWlsIjogImZlYW5pbEBheGltLm9yZyIsICJuYW1lIjogIkZlYW5pbCBQYXRlbCIsICJmYW1pbHlfbmFtZSI6ICIiLCAiZ2l2ZW5fbmFtZSI6ICIiLCAiYWRtaW5pc3RyYXRvciI6IHRydWUsICJzdXBlcnVzZXIiOiB0cnVlfQ.oNTEk7aMFSjvEbvH_-Gu2QZE93w-CpXSIIuN-IC6BSU', + 'expires_in': 3600, + 'token_type': 'JWT', + 'scope': 'read write email profile', + 'refresh_token': 'V5fbgDt2RPVnmI6Q3c6cJ3OjVriGii'} + +Use a JWT Header to call an API +******************************* + +.. code-block:: + + # See above examples for how to get a JWT token + access_token = token_request.json()["access_token"] + + enrollment_request = requests.get( + "http://localhost:8000/api/enrollment/v1/enrollment", + headers={"Authorization": f"JWT {access_token}"}, + ) + + pprint(enrollment_request.json()) + +.. code-block:: + :caption: Output + + [{'course_details': {'course_end': None, + 'course_id': 'course-v1:TestX+Course+1', + 'course_modes': [{'bulk_sku': None, + 'currency': 'usd', + 'description': None, + 'expiration_datetime': None, + 'min_price': 0, + 'name': 'Audit', + 'sku': None, + 'slug': 'audit', + 'suggested_prices': ''}], + 'course_name': 'Open edX Test Course', + 'course_start': '2022-04-09T00:00:00Z', + 'enrollment_end': None, + 'enrollment_start': None, + 'invite_only': False, + 'pacing_type': 'Instructor Paced'}, + 'created': '2023-08-17T14:10:48.476967Z', + 'is_active': True, + 'mode': 'audit', + 'user': 'test_user'}] diff --git a/docs/references/index.rst b/docs/references/index.rst index d1a1af3986..bdafc21688 100644 --- a/docs/references/index.rst +++ b/docs/references/index.rst @@ -4,6 +4,7 @@ References .. toctree:: :maxdepth: 1 :glob: + :caption: Table of Contents * docstrings/index