commit 46b28bc1660c8b3b000d368ff0054500dba84c64 Author: Cristiano Hoshikawa Date: Sat Dec 23 09:35:14 2023 -0300 first commit diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..531389e Binary files /dev/null and b/.DS_Store differ diff --git a/files/OAuthOCIService-fn.zip b/files/OAuthOCIService-fn.zip new file mode 100644 index 0000000..2571ad1 Binary files /dev/null and b/files/OAuthOCIService-fn.zip differ diff --git a/files/config b/files/config new file mode 100644 index 0000000..119634b --- /dev/null +++ b/files/config @@ -0,0 +1,7 @@ +[DEFAULT] +user=ocid1.user.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +fingerprint=a0:xx:xx:xx:xx:xx:xx:00:xx:xx:xx:xx:xxx:xxx:4b:9d +key_file=oci_api_key.pem +tenancy=ocid1.tenancy.oc1..aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +region=us-ashburn-1 + diff --git a/files/config.json b/files/config.json new file mode 100644 index 0000000..32aaa2d --- /dev/null +++ b/files/config.json @@ -0,0 +1,12 @@ +{ + "ClientId" : "e3xxxxxxxxxxxxxxxxxxxxxxxxc2f", + "ClientSecret" : "8exxxxxxd-fxxe-4xxf-8xxc-xxxxxxxx", + "BaseUrl" : "https://idcs-xxxxxxxxxxxxxxxxxxxxxxxx.identity.oraclecloud.com", + "AudienceServiceUrl" : "https://idcs-xxxxxxxxxxxxxxxxxxxxxxxx.identity.oraclecloud.com", + "scope" : "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com/super-scope", + "TokenIssuer" : "https://identity.oraclecloud.com", + "redirectURL": "http://localhost:8000/callback", + "logoutSufix":"/oauth2/v1/userlogout", + "LogLevel":"INFO", + "ConsoleLog":"True" +} \ No newline at end of file diff --git a/files/func.py b/files/func.py new file mode 100644 index 0000000..f133bf0 --- /dev/null +++ b/files/func.py @@ -0,0 +1,169 @@ +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from cryptography.hazmat.primitives.serialization import load_pem_private_key +import base64 +import json +from datetime import datetime +import io +from fdk import response +import oci +import requests + +def get_date(): + d = str(datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')) + return d + +def get_signing(d, streaming_host, oci_region): + with open('oci_api_key.pem', 'rb') as key_file: + private_key = load_pem_private_key(key_file.read(), password=None)# Dados para assinar + str = b'(request-target): post /20180418/streams//groupCursors\ndate: \nhost: cell-1.streaming..oci.oraclecloud.com'# Assine os dados usando SHA-256 e a chave privada + + data = str.replace(b'', bytes(d.encode())).replace(b'', bytes(streaming_host.encode())).replace(b'', bytes(oci_region.encode())) + signature = private_key.sign(data, padding.PKCS1v15(), hashes.SHA256())# Imprima a assinatura + + base64_encoded = base64.b64encode(signature) + + return base64_encoded + +def get_authorization(d, streaming_host, oci_region, tenancy, user, fingerprint): + a = get_signing(d, streaming_host, oci_region) + s = b'Signature algorithm="rsa-sha256",headers="(request-target) date host",keyId="//",signature="",version="1"' + s = s.replace(b'', a).replace(b'', bytes(tenancy.encode())).replace(b'', bytes(user.encode())).replace(b'', bytes(fingerprint.encode())) + r = s.decode() + return r + +def auth_idcs(token, url, clientID, secretID): + url = url + "/oauth2/v1/introspect" + + auth = clientID + ":" + secretID + auth_bytes = auth.encode("ascii") + auth_base64_bytes = base64.b64encode(auth_bytes) + auth_base64_message = auth_base64_bytes.decode("ascii") + + headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ' + auth_base64_message + } + + payload = "token=" + token + + response = requests.request("POST", url, headers=headers, data=payload) + return response + +#Function used to load the configurations from the config.json file +def getOptions(): + fo = open("config.json", "r") + config = fo.read() + options = json.loads(config) + return options + +def handler(ctx, data: io.BytesIO = None): + config = oci.config.from_file("config") + logging = oci.loggingingestion.LoggingClient(config) + tenancy = config['tenancy'] + user = config['user'] + fingerprint = config['fingerprint'] + + app_context = dict(ctx.Config()) + streaming_host = app_context['streaming_host'] + oci_region = app_context['oci_region'] + jsonData = "" + + options = getOptions() + + try: + header = json.loads(data.getvalue().decode('utf-8'))["data"] + access_token = header["token"] + url = options["BaseUrl"] + + authorization = auth_idcs(access_token, url, options["ClientId"], options["ClientSecret"]) + if authorization.json().get("active") == False: + return response.Response( + ctx, + status_code=401, + response_data=json.dumps({"active": False, "wwwAuthenticate": jsonData}) + ) + + d = get_date() + a = get_authorization(d, streaming_host=streaming_host, oci_region=oci_region, tenancy=tenancy, user=user, fingerprint=fingerprint) + + rdata = json.dumps({ + "active": True, + "context": { + "date": d, + "authorization": a, + "streaming_host": streaming_host, + "oci_region": oci_region, + "tenancy": tenancy, + "user": user, + "fingerprint": fingerprint + }}) + + put_logs_response = logging.put_logs( + log_id="ocid1.log.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + put_logs_details=oci.loggingingestion.models.PutLogsDetails( + specversion="EXAMPLE-specversion-Value", + log_entry_batches=[ + oci.loggingingestion.models.LogEntryBatch( + entries=[ + oci.loggingingestion.models.LogEntry( + data="authorization: " + str(a), + id="ocid1.test.oc1..00000001.EXAMPLE-id-Value")], + source="EXAMPLE-source-Value", + type="EXAMPLE-type-Value")])) + + put_logs_response = logging.put_logs( + log_id="ocid1.log.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + put_logs_details=oci.loggingingestion.models.PutLogsDetails( + specversion="EXAMPLE-specversion-Value", + log_entry_batches=[ + oci.loggingingestion.models.LogEntryBatch( + entries=[ + oci.loggingingestion.models.LogEntry( + data="request payload: " + json.dumps(header), + id="ocid1.test.oc1..00000001.EXAMPLE-id-Value-1")], + source="EXAMPLE-source-Value", + type="EXAMPLE-type-Value")])) + + put_logs_response = logging.put_logs( + log_id="ocid1.log.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + put_logs_details=oci.loggingingestion.models.PutLogsDetails( + specversion="EXAMPLE-specversion-Value", + log_entry_batches=[ + oci.loggingingestion.models.LogEntryBatch( + entries=[ + oci.loggingingestion.models.LogEntry( + data="access: " + json.dumps(authorization.text), + id="ocid1.test.oc1..00000001.EXAMPLE-id-Value-1")], + source="EXAMPLE-source-Value", + type="EXAMPLE-type-Value")])) + + + return response.Response( + ctx, response_data=rdata, + status_code=200, + headers={"Content-Type": "application/json", "Authorization": a, "Date": d} + ) + + except(Exception) as ex: + jsonData = 'error parsing json payload: ' + str(ex) + put_logs_response = logging.put_logs( + log_id="ocid1.log.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + put_logs_details=oci.loggingingestion.models.PutLogsDetails( + specversion="EXAMPLE-specversion-Value", + log_entry_batches=[ + oci.loggingingestion.models.LogEntryBatch( + entries=[ + oci.loggingingestion.models.LogEntry( + data="error: " + jsonData, + id="ocid1.test.oc1..00000001.EXAMPLE-id-Value")], + source="EXAMPLE-source-Value", + type="EXAMPLE-type-Value")])) + + pass + + return response.Response( + ctx, + status_code=401, + response_data=json.dumps({"active": False, "wwwAuthenticate": jsonData}) + ) \ No newline at end of file diff --git a/files/func.yaml b/files/func.yaml new file mode 100644 index 0000000..830d028 --- /dev/null +++ b/files/func.yaml @@ -0,0 +1,8 @@ +schema_version: 20180708 +name: get-authorization +version: 0.0.408 +runtime: python +build_image: fnproject/python:3.9-dev +run_image: fnproject/python:3.9 +entrypoint: /python/bin/fdk /function/func.py handler +memory: 256 diff --git a/files/oci_api_key.pem b/files/oci_api_key.pem new file mode 100644 index 0000000..8aeca0e --- /dev/null +++ b/files/oci_api_key.pem @@ -0,0 +1,28 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAr7/go+lbpX2toGkCfFMX2UD/EKWXt+upllj2o0g43BFQ2JVJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +GMDKZf1FkmgNburj3zZQnLrkHwBvB4pozWy6B1Lbkd+SBQcuuP87g5fkDcExPgNJ +88y+gQKBgQDNmDZsl7fAHupNuFQdQphJ+TI8EbZMfH+i9ChZ41coKTD+e2ucc8ll +ocjWmoLuyXAIhcTforN+l+c0z7ZiBZ+kAz1hD4DSMrrhxIuZIodIh3myUMCAzQv6 +hZsTonAFY2y20Ql6v/rvYpTMsiUvMVI/Jx43jBcPunGHn/asLmfLZg== +-----END RSA PRIVATE KEY----- diff --git a/files/requirements.txt b/files/requirements.txt new file mode 100644 index 0000000..8f0de40 --- /dev/null +++ b/files/requirements.txt @@ -0,0 +1,8 @@ +fdk>=0.1.54 +requests +oci +cryptography +six +PyJWT +py3_lru_cache +simplejson diff --git a/images/activate_1.png b/images/activate_1.png new file mode 100644 index 0000000..8d13dcf Binary files /dev/null and b/images/activate_1.png differ diff --git a/images/api_endpoint.png b/images/api_endpoint.png new file mode 100644 index 0000000..b5d8518 Binary files /dev/null and b/images/api_endpoint.png differ diff --git a/images/apigw-1.png b/images/apigw-1.png new file mode 100644 index 0000000..839262a Binary files /dev/null and b/images/apigw-1.png differ diff --git a/images/apigw-1a.png b/images/apigw-1a.png new file mode 100644 index 0000000..b14f986 Binary files /dev/null and b/images/apigw-1a.png differ diff --git a/images/apigw-2.png b/images/apigw-2.png new file mode 100644 index 0000000..00b9935 Binary files /dev/null and b/images/apigw-2.png differ diff --git a/images/apigw-3.png b/images/apigw-3.png new file mode 100644 index 0000000..86b877a Binary files /dev/null and b/images/apigw-3.png differ diff --git a/images/client_app_1.png b/images/client_app_1.png new file mode 100644 index 0000000..8d91aa3 Binary files /dev/null and b/images/client_app_1.png differ diff --git a/images/client_app_2.png b/images/client_app_2.png new file mode 100644 index 0000000..713d531 Binary files /dev/null and b/images/client_app_2.png differ diff --git a/images/client_app_3.png b/images/client_app_3.png new file mode 100644 index 0000000..f17c126 Binary files /dev/null and b/images/client_app_3.png differ diff --git a/images/client_app_4.png b/images/client_app_4.png new file mode 100644 index 0000000..76edeab Binary files /dev/null and b/images/client_app_4.png differ diff --git a/images/client_app_5.png b/images/client_app_5.png new file mode 100644 index 0000000..b86b6e1 Binary files /dev/null and b/images/client_app_5.png differ diff --git a/images/client_app_6.png b/images/client_app_6.png new file mode 100644 index 0000000..e19b2c6 Binary files /dev/null and b/images/client_app_6.png differ diff --git a/images/code_1.png b/images/code_1.png new file mode 100644 index 0000000..29acad2 Binary files /dev/null and b/images/code_1.png differ diff --git a/images/confidentialapp.png b/images/confidentialapp.png new file mode 100644 index 0000000..30773d1 Binary files /dev/null and b/images/confidentialapp.png differ diff --git a/images/federation.png b/images/federation.png new file mode 100644 index 0000000..1bae577 Binary files /dev/null and b/images/federation.png differ diff --git a/images/idcs.png b/images/idcs.png new file mode 100644 index 0000000..7b004b2 Binary files /dev/null and b/images/idcs.png differ diff --git a/images/idcs_add.png b/images/idcs_add.png new file mode 100644 index 0000000..3864d1f Binary files /dev/null and b/images/idcs_add.png differ diff --git a/images/idcs_config_access.png b/images/idcs_config_access.png new file mode 100644 index 0000000..9158b7b Binary files /dev/null and b/images/idcs_config_access.png differ diff --git a/images/idcs_link.png b/images/idcs_link.png new file mode 100644 index 0000000..05d260c Binary files /dev/null and b/images/idcs_link.png differ diff --git a/images/idcs_link2.png b/images/idcs_link2.png new file mode 100644 index 0000000..d7cbcac Binary files /dev/null and b/images/idcs_link2.png differ diff --git a/images/img.png b/images/img.png new file mode 100644 index 0000000..f3ac672 Binary files /dev/null and b/images/img.png differ diff --git a/images/img_1.png b/images/img_1.png new file mode 100644 index 0000000..ccc15d1 Binary files /dev/null and b/images/img_1.png differ diff --git a/images/img_10.png b/images/img_10.png new file mode 100644 index 0000000..594a3f7 Binary files /dev/null and b/images/img_10.png differ diff --git a/images/img_11.png b/images/img_11.png new file mode 100644 index 0000000..3fee290 Binary files /dev/null and b/images/img_11.png differ diff --git a/images/img_12.png b/images/img_12.png new file mode 100644 index 0000000..09658e3 Binary files /dev/null and b/images/img_12.png differ diff --git a/images/img_13.png b/images/img_13.png new file mode 100644 index 0000000..3e631d3 Binary files /dev/null and b/images/img_13.png differ diff --git a/images/img_14.png b/images/img_14.png new file mode 100644 index 0000000..a0e6ca1 Binary files /dev/null and b/images/img_14.png differ diff --git a/images/img_15.png b/images/img_15.png new file mode 100644 index 0000000..742b4a6 Binary files /dev/null and b/images/img_15.png differ diff --git a/images/img_16.png b/images/img_16.png new file mode 100644 index 0000000..8c1d43d Binary files /dev/null and b/images/img_16.png differ diff --git a/images/img_2.png b/images/img_2.png new file mode 100644 index 0000000..4b39f03 Binary files /dev/null and b/images/img_2.png differ diff --git a/images/img_3.png b/images/img_3.png new file mode 100644 index 0000000..5467eb3 Binary files /dev/null and b/images/img_3.png differ diff --git a/images/img_4.png b/images/img_4.png new file mode 100644 index 0000000..fa988dc Binary files /dev/null and b/images/img_4.png differ diff --git a/images/img_5.png b/images/img_5.png new file mode 100644 index 0000000..72f5bcc Binary files /dev/null and b/images/img_5.png differ diff --git a/images/img_6.png b/images/img_6.png new file mode 100644 index 0000000..c327e5d Binary files /dev/null and b/images/img_6.png differ diff --git a/images/img_7.png b/images/img_7.png new file mode 100644 index 0000000..400ca75 Binary files /dev/null and b/images/img_7.png differ diff --git a/images/img_8.png b/images/img_8.png new file mode 100644 index 0000000..4b5197b Binary files /dev/null and b/images/img_8.png differ diff --git a/images/img_9.png b/images/img_9.png new file mode 100644 index 0000000..c2cf202 Binary files /dev/null and b/images/img_9.png differ diff --git a/images/jwk.png b/images/jwk.png new file mode 100644 index 0000000..183e895 Binary files /dev/null and b/images/jwk.png differ diff --git a/images/model_deploy_2.png b/images/model_deploy_2.png new file mode 100644 index 0000000..fa452f3 Binary files /dev/null and b/images/model_deploy_2.png differ diff --git a/images/modeldeploy_1.png b/images/modeldeploy_1.png new file mode 100644 index 0000000..2c0fdeb Binary files /dev/null and b/images/modeldeploy_1.png differ diff --git a/images/oauth_body.png b/images/oauth_body.png new file mode 100644 index 0000000..d1dffe6 Binary files /dev/null and b/images/oauth_body.png differ diff --git a/images/oauth_resource_1.png b/images/oauth_resource_1.png new file mode 100644 index 0000000..196f7ae Binary files /dev/null and b/images/oauth_resource_1.png differ diff --git a/images/policy_1.png b/images/policy_1.png new file mode 100644 index 0000000..0bdbba8 Binary files /dev/null and b/images/policy_1.png differ diff --git a/images/policy_2.png b/images/policy_2.png new file mode 100644 index 0000000..8640da8 Binary files /dev/null and b/images/policy_2.png differ diff --git a/images/postman-1.png b/images/postman-1.png new file mode 100644 index 0000000..9e2695f Binary files /dev/null and b/images/postman-1.png differ diff --git a/images/postman_3.png b/images/postman_3.png new file mode 100644 index 0000000..f41521e Binary files /dev/null and b/images/postman_3.png differ diff --git a/images/postman_6.png b/images/postman_6.png new file mode 100644 index 0000000..e25b229 Binary files /dev/null and b/images/postman_6.png differ diff --git a/images/resource_1.png b/images/resource_1.png new file mode 100644 index 0000000..08a65aa Binary files /dev/null and b/images/resource_1.png differ diff --git a/images/resource_2.png b/images/resource_2.png new file mode 100644 index 0000000..61877e6 Binary files /dev/null and b/images/resource_2.png differ diff --git a/images/resource_server_1.png b/images/resource_server_1.png new file mode 100644 index 0000000..068df5a Binary files /dev/null and b/images/resource_server_1.png differ diff --git a/images/resource_server_2.png b/images/resource_server_2.png new file mode 100644 index 0000000..ed0317b Binary files /dev/null and b/images/resource_server_2.png differ diff --git a/images/result.png b/images/result.png new file mode 100644 index 0000000..b39acbe Binary files /dev/null and b/images/result.png differ diff --git a/images/scope.png b/images/scope.png new file mode 100644 index 0000000..c527d8e Binary files /dev/null and b/images/scope.png differ diff --git a/images/scope_2.png b/images/scope_2.png new file mode 100644 index 0000000..7a8913f Binary files /dev/null and b/images/scope_2.png differ diff --git a/images/select_resource_server.png b/images/select_resource_server.png new file mode 100644 index 0000000..d4fe018 Binary files /dev/null and b/images/select_resource_server.png differ diff --git a/index.md b/index.md new file mode 100644 index 0000000..edf105e --- /dev/null +++ b/index.md @@ -0,0 +1,402 @@ +--- +duration: PT1H00M0S +description: Learn how to use Oracle Cloud API Gateway to expose OCI Streaming with other Authorization Methods +level: Advanced +roles: Devops;Developer +products: en/cloud/oracle-cloud-infrastructure/oci +keywords: APIs SOAP/REST +inject-note: true +--- + +# Learn how to use Oracle Cloud API Gateway to expose OCI Streaming with other Authorization Methods + +## Introduction + +We often need our applications to consume OCI REST services. There are several ways to guarantee security between components, ensuring that the application can authenticate securely to the backend service. + +Most of the time, this task is native within the Oracle Cloud, as there are several ways to secure the network and access to existing services. Just a few settings and you're done. + +However, there are cases where the application may offer additional security and connectivity requirements. + +The use case of this material meets a very common need in the hybrid or multi-cloud scenario (on-premises connected to the Oracle cloud, or Oracle cloud connected to another cloud). + +Let's present the following scenario: + +- Application on an on-premises network connected to Oracle Cloud through Fast-Connect/VPN +- Application needs to consume an OCI Streaming service +- OCI service does not have an authentication mechanism that meets the application consumer's possibilities +- Application needs to authenticate using OAuth2 to be able to access the service securely + +Therefore, the material provides the following solution: + +- Configure the Oracle IDCS cloud's own Identity Provider to authenticate through OAuth2 +- Configure OCI API Gateway to integrate with IDCS to authenticate via an obtained token +- Code an fn to produce the Authentication for the OCI Streaming (**draft-cavage-http-signatures-08 method**) +- Create groups and policies to limit access to cloud resources +- Deliver an Identity Provider that allows you to pass the Client ID and Secret ID and obtain an authentication token +- Deliver a functional API Gateway REST service that authenticates through the obtained token and provides the consumer to use the Streaming Services + +>**Note**: The **OCI fn** code can be downloaded [here](./files/OAuthOCIService-fn.zip) + +## Objectives + +- Allow an external application to consume REST services with OAuth2 authentication +- Provide an OAuth2 authentication service on OCI +- Configure OCI API Gateway and fn to run OCI services via a token + +## Prerequisites + +- An OCI API Gateway instance created and exposed to the Internet, see [Creating Your First API Gateway In The Oracle Cloud](https://blogs.oracle.com/developers/post/creating-your-first-api-gateway-in-the-oracle-cloud). +- Network Connectivity between OCI API Gateway, fn and OCI PaaS Resource + - VCN/Subnets + - Security List + - Nat Gateway/Internet Gateway + - Public/Private Networks +- Knowledge with the + - OCI Functions + - OCI REST API to code a call for the OCI Service + +## Task 1: Configure OAuth2 with IDCS + +### Obtain the OCI API Gateway parameters + +Let's start to configure the OAuth2 mechanism. We need to integrate your OCI API Gateway instance to an Identity Provider, in this example, we will configure the IDCS from Oracle Cloud to be these identity provider. + +Go to the OCI API Gateway Instance and copy your hostname. This information will be used in your IDCS resource server configuration in the next step. + +![img.png](images/resource_2.png) + +### Create a Resource Application + +Now we need to create an OAuth2 authorizer for your application. +We can do it with the IDCS in Oracle Cloud. + +In the OCI Console, go to "Identity & Security" and select "Federation". + +![img.png](images/federation.png) + +Now click in the "OracleIdentityCloudSevice" link. + +![img.png](images/idcs_link.png) + +And click in the link for your IDCS instance. + +![img.png](images/idcs_link2.png) + +Now, we will create 2 applications. Click in the Applications and Services option. + +![img.png](images/idcs.png) + +In the Applications, click the Add button + +![img.png](images/idcs_add.png) + +Select "Confidential Application" to start to configure your Resource Server. + +![img.png](images/confidentialapp.png) + +Now we will configure the first application. Put a name in your resource server application and click Next. + +![img.png](images/resource_server_1.png) + +Skip this step. We need to configure the resource only. + +![img.png](images/resource_server_2.png) + +Now, put your OCI API Gateway hostname obtained in the last step + +![img.png](images/resource_1.png) + +Click in the Add Scope button and fill with a scope information. + +![img.png](images/scope.png) + +Verify your scope information and click Next 2 times and finally click Finish. + +![img.png](images/scope_2.png) + +Activate your application. + +![img.png](images/activate_1.png) + +### Create a Client Application + +In the Applications, click the Add button + +![img.png](images/idcs_add.png) + +Select "Confidential Application" to start to configure your Resource Server. + +![img.png](images/confidentialapp.png) + +Put a name for your application and click Next button + +![img_1.png](images/client_app_1.png) + +Select "Configure the application as a cliente now" to enable the configurations for your client application. +After this, you will see the parameters. + +Now, select "Client Credentials" and "JWT Assertion" options and "On behalf of". Don't click Next yet. + +![img.png](images/client_app_2.png) + +>**Note:** If you want to validate OAuth2 in your function code, you MUST set Introspect flag. This will enable the /oauth2/v1/introspect IDCS REST API Service + +![img_10.png](images/img_10.png) + +Roll down the screen and click in the "Add Scope" button. + +![img.png](images/client_app_3.png) + +Find your Resource Application created before (oauth_resource_server in this example) and click Add button. + +![img.png](images/select_resource_server.png) + +You can see your scope added to your application. Click Next button + +![img.png](images/client_app_4.png) + +>**Note**: Keep the scope value, you will need to use to request a token + +Skip the Resources and the Web Tier Policy step. In the last step, select "Enforce Grants as Authorization" option and click Finish button. + +![img.png](images/client_app_5.png) + +Keep the Client ID and the Client Secret information. You will need this to obtain your Token. + +![img.png](images/client_app_6.png) + +Activate your application and your OAuth2 authorizer is ready to test + +![img_1.png](images/oauth_resource_1.png) + +### Get a token + +Now we can test the OAuth2 Authorizer to obtain the token. + +The first step is to compose the URL for the authorizer. You can obtain this by getting your IDCS url in the browser. +In the IDCS URL, you can see something like this: + + https://idcs-xxxxxxxxxxxxx.identity.oraclecloud.com/ui/v1/adminconsole + +You will need the URL link until the oraclecloud.com. So, this is the root endpoint: + + https://idcs-xxxxxxxxxxxxx.identity.oraclecloud.com + +Now, we need to add the oauth authentication path. This URL will be executed as a POST REST request. + + https://idcs-xxxxxxxxxxxxx.identity.oraclecloud.com/oauth2/v1/token + +You will need to put some paramters to request the token. + +First, put the Credentials as a Basic Authentication. You will put the Client ID and your Client Secret. + +![img.png](images/postman-1.png) + +Now, in the Body content, fill with the grant_type and scope values. +Remember, the scope was captured in the IDCS configuration + +![img.png](images/oauth_body.png) + +Execute the POST Request and view the Token + +![img_2.png](images/postman_3.png) + + +## Task 2: Configure a fn to call your OCI SDK API + +### Understand the OCI Functions and API Gateway + +You can download the Example of Python code here [OAuthOCIService-fn.zip](./files/OAuthOCIService-fn.zip) + +It's a best practice to expose your services through an API Gateway. Many authentications can be done bypassing the credentials from API Gateway to the backend services, but if the backend authentication was not the apropriate method to your client application, we can do some configurations in the API Gateway level. + +In this step, let's understand how **OCI API Gateway** can help us to integrate the OAuth2 authentication and the request for any OCI Service, like the OCI Streaming through the **OCI Functions**. + +**OCI Functions** can do the job to produce the correct authentication/authorization and pass to the OCI Service without the necessity of passing user credentials or private keys to the consumer's applications. Some services in the OCI Service cannot authenticate by OAuth2 method, so we can do it with OCI Functions. + +In this example, the OCI Streaming service can authenticate by the OCI Private key in OCI IAM through [draft-cavage-http-signatures-08 method](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-08) and consumers can authenticate by OAuth2. + + +If you don't know how to create and deploy a **OCI fn**, please see [OCI Functions Quickstart](https://docs.oracle.com/en-us/iaas/developer-tutorials/tutorials/functions/func-setup-cli/01-summary.htm) + +### Understand the code + +This code will be prepared to be used with **OCI API Gateway**. In your API Deployment, we will configure the OCI Streaming endpoint in the API Gateway and it will be passed as a HEADER Authorization parameter. So you can use this function for many Services in each API Gateway deployments you need. + +![img_12.png](images/img_12.png) + +This function in Python will create a date and time in format for the Authentication. This date and time need to be generated one time and used in 2 points. To the Header **Date** on the OCI Streaming request and to the **Signature** criptographed HEADER. + +![img_1.png](images/img_1.png) + +This is the function for Signature encryption. The assembly consists of using the OCI IAM user's private key with SHA-256 and then in base64 + +![img_2.png](images/img_2.png) + +The signature encryption will be used on the complete **Authorization** HEADER. + +![img_3.png](images/img_3.png) + +This is the **IDCS OAUTH2** token validation code. You need to configure the **config.json** file with your **IDCS** Application parameters. + +![img_13.png](images/img_13.png) + +This is the beginning of the function and initializes some information from OCI IAM security and OCI Streaming variables created for this API Deployment (**streaming_host** and **oci_region**) + +![img_4.png](images/img_4.png) + +This is the main part of this function, there is 2 parts: + +In the first part, we obtain the token passed in the **OCI API Gateway** request. After this, we call the **auth_idcs()** function to validade the token in **IDCS**. Before generate the **draft-cavage-http-signatures-08** string, we validate the **IDCS** token. If it's not a valid token, the response will give a **401 unauthorized** error. + +In the second part we will create 2 values. +The date (d) with the now date format and it will be used in the creation of the **Signature** (a). +The rdata will construct the response for the function with the **authorization** and **date** values that will be used by **OCI API Gateway** to authenticate on **OCI Streaming**. + +![img_15.png](images/img_15.png) + +Here we can generate a Log in the **OCI Observability** and this step is optional. Please, provide de **OCID** for the Log. You need to create a **Custom Log** previously. + +![img_6.png](images/img_6.png) + +And this is the final step and return with a valid authentication. + +![img_7.png](images/img_7.png) + +This is the **requirements.txt** libraries that will need to be loaded in this (fn) functions. + + requirements.txt + --------------------- + fdk>=0.1.54 + requests + oci + cryptography + six + PyJWT + py3_lru_cache + simplejson + +This is the **config.json** file + +![img_14.png](images/img_14.png) + +And this is the **OCI** config file. You can copy this file from your **OCI CLI** installation but it's important to modify the **key_file** section, removing the path of your **OCI CLI** installation. + +This will be used in the Python code to generate the **draft-cavage-http-signatures-08** criptographic string + +![img_16.png](images/img_16.png) + +Before deploy the function, create 2 variables and fill with the **OCI Streaming OCID** and **OCI region** values like this: + + fn config app streaming_host ocid1.stream.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxqhgw275qp7a + fn config app oci_region us-ashburn-1 + + + +Deploy your function and let's configure it in the **OCI API Gateway** + +## Task 3: Configure an API Gateway Deployment + +> **Note**: If you don't know how to develop a function and call it in API Gateway, see +[Functions: Validate an API Key with API Gateway](https://docs.oracle.com/en-us/iaas/developer-tutorials/tutorials/functions/func-api-gtw-token/01-summary.htm) + +Configure your **OCI API Gateway** service. In this example, we will configure an **OCI Streaming** service. + +You can put the **Path prefix** with **/20180418/streams/** plus the **OCID** of your streaming instance + + /20180418/streams/ocid1.stream.oc1.iad.xxxxxxxxxxxxxxxxxxxxxxxxxxxx + +![img.png](images/apigw-1a.png) + +In the Authentication step, select **Single Authentication** and **Authorizer Function**. + +Select the compartment and your function **get-authorization**. + +![img.png](images/apigw-1.png) + +Now, configure your request HEADERs parameters. + + Header Parameter 1 + Context table=request.headers + Header name=Date + Argument name=Date + + Header Parameter 2 + Context table=request.headers + Header name=token + Argument name=token + +To configure the HEADER for IDCS parameters, you need to click on **Show route request policies** to open the options + +![img.png](images/apigw-3.png) + +In the **HEADER transformations**, click the Add button + +![img.png](images/apigw-2.png) + + Set Behavior to Overwrite in both parameters + + Authorization = ${request.auth[authorization]} + Date = ${request.auth[date]} + +The **${request.auth[authorization]}** and **${request.auth[date]}** are the values returned by the function Python code and will be passed as the HEADERs for the backend service. + +## Task 4: Configure the OCI Group and Policies + + +## Task 5: Test API + +Now let's simulate your Application OAuth2 request for your Model Deployment Service in **OCI Streaming**. + +First, obtain the token passing the **Client ID** and **Client Secret** to your IDCS Provider. + + Verb: POST + URL: https://idcs-xxxxxxxxxxxxxxxxxxxxxxx.identity.oraclecloud.com/oauth2/v1/token + BODY (x-www-form-urlencoded): + scope: xxxxxxxxxxxxxxxxxxx.apigateway.us-ashburn-1.oci.customer-oci.com/super-scope + grant_type: client_credentials + HEADER + Authorization: clientID:clientSecret* + * convert your clientID:clientSecret to a base64 value + +![img_2.png](images/postman_3.png) + +Now we can test the request to OCI API Gateway. + +Put your **OCI API Gateway** deployment endpoint and select **POST** verb to your REST request. + +You will need to declare 6 HEADERs in the BODY of your request + + Content-Type: application/json + access-control-allow-origin: * + access-control-allow-method: POST,PUT,GET,HEAD,DELETE,OPTIONS + access-control-allow-credentials: true + Date: put an actual date in this format -> Thu, 21 Dec 2023 13:53:59 GMT + token: put your here* + + * Remember that your token have 1 hour duration. + +![img_9.png](images/img_9.png) + +And here is the result !!! + +![img_11.png](images/img_11.png) +In the Logs, you can see the payloads for your custom code authorization logic. + +![img_8.png](images/img_8.png) + +## Related Links + +* [Creating Your First API Gateway In The Oracle Cloud](https://blogs.oracle.com/developers/post/creating-your-first-api-gateway-in-the-oracle-cloud) +* [OCI Functions Quickstart](https://docs.oracle.com/en-us/iaas/developer-tutorials/tutorials/functions/func-setup-cli/01-summary.htm) +* [Call a function using API Gateway](https://docs.public.oneportal.content.oci.oraclecloud.com/en-us/iaas/developer-tutorials/tutorials/functions/func-api-gtw/01-summary.htm) +* [Validate OAuth2 token](https://docs.oracle.com/en/cloud/paas/identity-cloud/rest-api/op-oauth2-v1-introspect-post.html) +* [Working with OAuth 2 to Access the REST API](https://docs.oracle.com/en/cloud/paas/identity-cloud/rest-api/OATOAuthClientWebApp.html) +* [Identity Cloud Services OAuth 2.0 and REST API](https://www.ateam-oracle.com/post/identity-cloud-services-oauth-20-and-rest-api) +* [Oracle Cloud Infrastructure (OCI) REST call walkthrough with curl](https://www.ateam-oracle.com/post/oracle-cloud-infrastructure-oci-rest-call-walkthrough-with-curl) + +## Acknowledgments + +* **Author** - Cristiano Hoshikawa (Oracle LAD A-Team Solution Engineer) +