# How To Generate & Use OCI Pre-Authenticated Requests from APEX

# Introduction
Sometimes you need to give a user or another system temporary access to get or upload files from/to Oracle Cloud Infrastructure (OCI) Object Storage. This temporary access is achieved using [Pre-Authenticated Requests](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/usingpreauthenticatedrequests.htm). Pre-Authenticated Requests (PARs) are pre-signed URLs that provide temporary access to read or write files from/to a specified bucket or a specified path within a bucket. This post will show you how to generate and consume PARs from within your APEX Applications.

> Pre-Authenticated Requests should be used with caution. A PAR URL gives anyone with the URL access to the Object Store resources identified in the request.

You can create a pre-authenticated request that grants read, write, or read/write access to one of the following:
- All objects in a bucket
- A specific object in a bucket
- All objects in a bucket that have a specified prefix

For PARs that apply to multiple objects, you can also decide whether you want to let users list those objects.

# Example Use Cases
- Where systems cannot authenticate into OCI using OCI Credentials
- Provide partners temporary access to upload files to a folder within a bucket
- Provide temporary access to customers to download new versions of Software

# Sample Code
I created a PL/SQL package and function which allows you to generate OCI Pre-Authenticated requests from APEX. You can download it from my [Github repository](https://github.com/jon-dixon/cn_blog_demos/tree/main/oci_pre_authenticated_requests).

# OCI Configurations
This section will describe the Oracle Cloud Infrastructure (OCI) setups required for this solution.

## Dependencies
Please refer to my previous post [Secure APEX Access to OCI Object Storage](https://blog.cloudnueva.com/apex-secure-object-store-setup). This post will show you how to perform the following configurations in the OCI Console. I will reference the configuration names in quotes throughout this blog post:
- Creating an Object Store Compartment 'APEX_OCI_BLOG'
- Creating an Object Store Bucket 'APEX_OCI_BLOG_FILES'
- Creating a Service Account 'APEX_OCI_BLOG_Service_Account'
- Create a Security Group 'APEX_OCI_BLOG_Security_Group'

## Additional OCI Configurations
Once you have completed the steps from the previous section, create the following additional Policy in the same OCI Compartment.
![PAR_Create_Policy_Screenshot.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660416510753/yq2Mx-qNF.png align="left")

The first policy statement: `Allow group APEX_OCI_BLOG_Security_Group manage buckets in compartment APEX_OCI_BLOG where all {target.bucket.name='APEX_OCI_BLOG_FILES', any {request.permission='PAR_MANAGE'}}`, allows users that belong to the `APEX_OCI_BLOG_Security_Group` to create PARs in the `APEX_OCI_BLOG_FILES` bucket.

The second policy statement: `Allow group APEX_OCI_BLOG_Security_Group to manage objects in compartment APEX_OCI_BLOG where all {target.bucket.name='APEX_OCI_BLOG_FILES', any {request.permission='OBJECT_CREATE', request.permission='OBJECT_READ',request.permission='OBJECT_OVERWRITE'}}`, allows users that belong to the `APEX_OCI_BLOG_Security_Group` to create, update and read objects in the `APEX_OCI_BLOG_FILES` bucket.

As the [documentation](https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/usingpreauthenticatedrequests.htm) mentions, not only must the user have access to create PARs, but they must also have permission to perform the actions passed on in the PAR (i.e., OBJECT_CREATE, OBJECT_READ, etc.). You can adjust the permissions in the second policy statement to suit your use case.

# APEX Code and Configurations
This section will examine the PL/SQL code and APEX configurations needed to support this solution.

## APEX Web Credential
Please refer to my previous post [Secure APEX Access to OCI Object Storage](https://blog.cloudnueva.com/apex-secure-object-store-setup). This post describes creating a Web Credential in APEX for your OCI Service Account. For this post, the Web Credential static ID is `APEX_OCI_BLOG_CREDENTIAL`.

## PL/SQL Function to Generate a PAR
In the package `cndemo_oci_pre_auth_requests_pk` you will see a function called `generate_pre_auth_request`. This function can be called from APEX to generate a PAR URL. You can see example calls to this function later in this post.

## PL/SQL Block to Generate a PAR
The PL/SQL block below borrows the logic from the function `generate_pre_auth_request` and makes it available in a PL/SQL block. The in-line comments describe what the code is doing.

%[https://gist.github.com/jon-dixon/57ca712d0c0dcc69953c23dc7c84a210]

** OCI REST Service Documentation Links**
- 📖 [Create Pre-Authenticated Request REST Service](https://docs.oracle.com/en-us/iaas/api/#/en/objectstorage/20160918/PreauthenticatedRequest/CreatePreauthenticatedRequest)
- 📖 [Create Pre-Authenticated Request REST Service Payload](https://docs.oracle.com/en-us/iaas/api/#/en/objectstorage/20160918/datatypes/CreatePreauthenticatedRequestDetails)
- 📖 [OCI Object Store REST Service Endpoints for your Region](https://docs.oracle.com/en-us/iaas/api/#/en/objectstorage/20160918/).

# Using a PAR
Now that we can generate PARs, how do we use these secured URLs to get and upload files from/to OCI Object Store? This section describes two examples, one to write files to a folder in a bucket and another to get a specific file from a particular folder.

## Example to Create/Upload Files
In this example, we generate a PAR to allow customers to upload files to Object Store using a secured URL.
- Target Bucket Name: APEX_OCI_BLOG_FILES
- Target Folder in Bucket Root: PAR_TEST_FOLDER

Code to generate PAR for this example:
```SQL
DECLARE
  l_par_url   VARCHAR2(1000);
BEGIN
  l_par_url := cndemo_oci_pre_auth_requests_pk.generate_pre_auth_request
    -- PAR will expire in 1 Hour
   (p_expiration_unit   => 'HOUR',
    p_expiration_value  => 1,
    -- PAR allows the holder of the URL to create any file
    p_access_type       => 'AnyObjectWrite',
    -- PAR restricts the holder of the URL to create files in the PAR_TEST_FOLDER folder
    p_object_name       => 'PAR_TEST_FOLDER',
    -- PAR allows the holder to list files in the folder PAR_TEST_FOLDER.
    p_object_listing    => 'ListObjects');
  dbms_output.put_line ('URL: '||l_par_url);
END;

-- Parameters:
-- p_expiration_unit   Values: [DAY,HOUR,MINUTE,SECOND]
-- p_expiration_value  Number of p_expiration_unit used to calculate URL expiration time.
-- p_access_type       Permissions: [ObjectRead, ObjectWrite, ObjectReadWrite, AnyObjectWrite, AnyObjectRead, AnyObjectReadWrite]
-- p_object_name       Object name or Prefix of objects in the bucket that URL has access to.
-- p_object_listing    Allow URL to list-objects. Values: [Deny,ListObjects]
```

PAR Generated by the above code: `https://objectstorage.us-ashburn-1.oraclecloud.com/p/QzYvrxQ2m3fwUbRELr2auXYIyDYkWxsMeNj1f3JjSKkXU_9P8XrR4RymKQfG3FYn/n/nueva/b/APEX_OCI_BLOG_FILES/o/`

In our example, we need to append the folder name (p_object_name) and the name we want the file to be called when uploaded to the folder. The completed URL looks like this:
`https://objectstorage.us-ashburn-1.oraclecloud.com/p/QzYvrxQ2m3fwUbRELr2auXYIyDYkWxsMeNj1f3JjSKkXU_9P8XrR4RymKQfG3FYn/n/nueva/b/APEX_OCI_BLOG_FILES/o/PAR_TEST_FOLDER/APEX_Logo_Dark.png`

The screenshot below shows how you can upload a file using the above PAR in Postman:
![Postman_PUT_File_Using_PAR_Example.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660433170309/boZE3FDXj.png align="left")

Here is the curl generated by Postman:
```
curl --location --request PUT 'https://objectstorage.us-ashburn-1.oraclecloud.com/p/QzYvrxQ2m3fwUbRELr2auXYIyDYkWxsMeNj1f3JjSKkXU_9P8XrR4RymKQfG3FYn/n/nueva/b/APEX_OCI_BLOG_FILES/o/PAR_TEST_FOLDER/APEX_Logo_Dark.png' \
--header 'Content-Type: image/png' \
--data-binary '@/Users/jdixon/Documents/Technical/My Blogs/Graphics/APEX Logos/APEX_Logo_Dark.png'
```

Here is a screenshot of the file that was uploaded to OCI Object Store:
![Object_Store_SHowing_Uploaded_File.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660433644260/bc3-RI5yM.png align="left")

## Example to Get a File
In this example, we are generating a PAR to allow customers to **download** a specific file from a particular folder in Object Store.
- Target Bucket Name: APEX_OCI_BLOG_FILES
- Target Folder and File: PAR_TEST_FOLDER/APEX_Logo_Light.png

Here is a screenshot of the file we want holders of the PAR to access:
![Object_Store_Showing_File_Get.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660433870134/xFol-l3HL.png align="left")

Code to generate PAR for this example:
```SQL
DECLARE
  l_par_url   VARCHAR2(1000);
BEGIN
  l_par_url := cndemo_oci_pre_auth_requests_pk.generate_pre_auth_request
    -- PAR will expire in 1 Hour
   (p_expiration_unit   => 'HOUR',
    p_expiration_value  => 1,
    -- PAR allows the holder of the URL to get a file
    p_access_type       => 'ObjectRead',
    -- PAR is restricted to the APEX_Logo_Light.png file 
    --  in the PAR_TEST_FOLDER folder.
    p_object_name       => 'PAR_TEST_FOLDER/APEX_Logo_Light.png',
    -- PAR does not allow the holder to list files.
    p_object_listing    => 'Deny');
  dbms_output.put_line ('URL: '||l_par_url);
END;
```

PAR Generated by the above code: `https://objectstorage.us-ashburn-1.oraclecloud.com/p/q7CeVldJj7d5pabY16kTWsREfr8NgjTRMAbBpNpBmAIPT2KYCyvIJK4o77fKqbrx/n/nueva/b/APEX_OCI_BLOG_FILES/o/PAR_TEST_FOLDER/APEX_Logo_Light.png`

The screenshot below shows how to get the file using the above PAR in Postman:
![Postman_GET_File_Using_PAR_Example.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1660434154116/LkY2XRuqM.png align="left")

Here is the curl generated by Postman:
```
curl --location --request GET 'https://objectstorage.us-ashburn-1.oraclecloud.com/p/q7CeVldJj7d5pabY16kTWsREfr8NgjTRMAbBpNpBmAIPT2KYCyvIJK4o77fKqbrx/n/nueva/b/APEX_OCI_BLOG_FILES/o/PAR_TEST_FOLDER/APEX_Logo_Light.png'
```

Using a PAR URL to get a file from PL/SQL is easy with [apex_web_service.make_rest_request_b](https://docs.oracle.com/en/database/oracle/apex/22.1/aeapi/MAKE_REST_REQUEST_B-Function.html#GUID-1FCD717A-A515-4394-832C-980D2990564F)

```SQL
DECLARE
  l_par_url       VARCHAR2(1000);
  l_response_blob BLOB;
BEGIN
  -- For a GET, use the complete URL returned from generating the PAR
  l_par_url := 'https://objectstorage.us-ashburn-1.oraclecloud.com/p/6gLghRVH8_NqRy2goZdhp-p0Ifr5JlR2YrkBObNjBJ2BmSS1s5hXysgzoFFV7WOX/n/test/b/APEX_OCI_BLOG_FILES/o/PAR_TEST_FOLDER/APEX_Logo_Light.png';
  -- No additional authentication or credentials are required
  l_response_blob := apex_web_service.make_rest_request_b
   (p_url         => l_par_url,
    p_http_method => 'GET');
  dbms_output.put_line ('File Size: '||dbms_lob.getlength(l_response_blob));
END;
```

# Conclusion
In this post, I described how Pre-authenticated requests allow you to provide temporary secure access to read or write files from/to OCI Object Store. I also showed you how you can generate and consume Pre-authenticated requests directly from your APEX applications.

## 🔗 Read More
- 🩳 [APEX Shorts](https://blog.cloudnueva.com/series/apex-shorts)
- #️⃣ [APEX Posts](https://blog.cloudnueva.com/tag/orclapex)
- 📖 [Secure APEX Access to OCI Object Storage}(https://blog.cloudnueva.com/apex-secure-object-store-setup)

