Skip to content

Commit a8038e7

Browse files
authored
Merge pull request oracle-samples#45 from WunderlichRd/master
Added BasicAuth sample created by Inchara Shamanna
2 parents 486f226 + cadf8d8 commit a8038e7

21 files changed

+476
-3
lines changed

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ This repository provides examples demonstrating how to use Oracle Functions.
3131
| Send an email using Email Delivery Service |[sample](./samples/oci-email-send-python)|
3232
| Decrypt cipher using Vault keys |[sample](./samples/oci-vault-decrypt-python)
3333
| Get a secret from Vault |[sample](./samples/oci-vault-get-secret-python)|
34-
| API Gateway Function authorizer for IDCS | |[sample](./samples/oci-apigw-authorizer-idcs-java)
35-
| Function that returns the API Gateway HTTP request information |[sample](./samples/oci-apigw-display-httprequest-info-python)
36-
| Function for API Gateway that validates an API key |[sample](./samples/oci-apigw-apikey-validation-python)
3734
| Write IAM policies that enables Functions in a tenancy to access resources in other tenancies ||[sample](./samples/oci-cross-tenancy-policies-java)
3835
| Trace a function with APM and add custom child spans using Zipkin |[sample](./samples/trace-functions-with-apm)|
3936

37+
## Functions and API Gateway
38+
[Oracle Cloud Functions](https://www.oracle.com/cloud-native/functions/) and [OCI API Gateway](https://www.oracle.com/api) together enable developers to create serverless APIs and perform function based authorization.
39+
| Description | Python | Java |
40+
|------------------------------------------------------|:------:|:----:|
41+
| API Gateway Function authorizer for IDCS | |[sample](./samples/oci-apigw-authorizer-idcs-java) |
42+
| Function that returns the API Gateway HTTP request information for testing | [sample](./samples/oci-apigw-display-httprequest-info-python) | |
43+
| Validate an API key |[sample](./samples/oci-apigw-apikey-validation-python) | |
44+
| BasicAuth Validation with IDCS | |[sample](./samples/oci-apigw-idcs-auth-basic) |
4045

4146

4247
## Using Service Connector Hub with Functions
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# API Gateway Basicauth function using IDCS
2+
This function provides verification of username and password against IDCS at runtime and allows only authorized users to access API gateway deployment.
3+
4+
The implementation conforms to the guidelines in the OCI Documentation at https://docs.cloud.oracle.com/en-us/iaas/Content/APIGateway/Tasks/apigatewayusingauthorizerfunction.htm.
5+
6+
As you make your way through this tutorial, look out for this icon ![user input icon](./images/userinput.png).
7+
Whenever you see it, it's time for you to perform an action.
8+
9+
10+
## Prerequisites
11+
12+
[Create users in IDCS](https://docs.oracle.com/en/cloud/paas/identity-cloud/uaids/create-user-accounts.html)
13+
14+
Before you deploy this sample function, make sure you have run step A, B and C of the [Oracle Functions Quick Start Guide for Cloud Shell](https://www.oracle.com/webfolder/technetwork/tutorials/infographics/oci_functions_cloudshell_quickview/functions_quickview_top/functions_quickview/index.html)
15+
* A - Set up your tenancy
16+
* B - Create application
17+
* C - Set up your Cloud Shell dev environment
18+
19+
## List Applications
20+
Assuming your have successfully completed the prerequisites, you should see your
21+
application in the list of applications.
22+
```
23+
fn ls apps
24+
```
25+
26+
## Deploy a function that implements an API
27+
We need another function that will be a target for API Gateway. We suggest [oci-display-httprequest-info-python](../oci-display-httprequest-info-python).
28+
In Cloud Shell, run the *fn deploy* command to build the function and its dependencies as a Docker image,
29+
push the image to OCIR, and deploy the function to Oracle Functions in your application.
30+
31+
![user input icon](./images/userinput.png)
32+
```
33+
cd ../oci-display-httprequest-info-python
34+
fn -v deploy --app <app-name>
35+
```
36+
37+
## Create or Update your Dynamic Group for API Gateway
38+
In order to invoke functions, your API Gateway must be part of a dynamic group.
39+
40+
When specifying the *Matching Rules*, we suggest matching all functions in a compartment with:
41+
```
42+
ALL {resource.type = 'ApiGateway', resource.compartment.id = 'ocid1.compartment.oc1..aaaaaxxxxx'}
43+
```
44+
45+
46+
## Create or Update IAM Policies for API Gateway
47+
Create a new policy that allows the API Gateway dynamic group to invoke functions. We will grant `use` access to `functions-family` in the compartment.
48+
49+
![user input icon](./images/userinput.png)
50+
51+
Your policy should look something like this:
52+
```
53+
Allow dynamic-group <dynamic-group-name> to use functions-family in compartment <compartment-name>
54+
```
55+
56+
For more information on how to create policies, check the [documentation](https://docs.cloud.oracle.com/iaas/Content/Identity/Concepts/policysyntax.htm).
57+
58+
59+
## Configure Identity Cloud Service (IDCS)
60+
Login to IDCS admin console and create, add an Application and select "Confidential Application".
61+
![IDCS-appcreate0](./images/IDCS-appcreate0.png)
62+
63+
Enter a name for your IDCS Application, for example "myAPI".
64+
65+
![IDCS-appcreate1](./images/IDCS-appcreate1.png)
66+
67+
For "Allowed Grant Types", select "Resource Owner". Click *Next*.
68+
69+
![IDCS-appcreate2](./images/IDCS-appcreate2.png)
70+
71+
For Primary Audience, enter anything "display-httprequest-info" for example.
72+
For Scopes, click *Add*. In the dialog box, for field "Scope", enter anything "display-httprequest-info" for example, click *Add*.
73+
74+
![IDCS-appcreate3](./images/IDCS-appcreate3.png)
75+
76+
Click *Next*.
77+
78+
![IDCS-appcreate4](./images/IDCS-appcreate4.png)
79+
80+
Click *Finish*.
81+
82+
![IDCS-appcreate5](./images/IDCS-appcreate5.png)
83+
84+
Now that the application is added, note the *Client ID* and *Client Secret*.
85+
86+
![IDCS-appcreate6](./images/IDCS-appcreate6.png)
87+
88+
Click *Close*.
89+
90+
Click on Configurations tab under Client Information section click on add scope and select the *application name* from the dropdown. Note the scope value.
91+
92+
![IDCS-appcreate7](./images/IDCS-appcreate7.png)
93+
![IDCS-appcreate8](./images/IDCS-appcreate8.png)
94+
95+
Click *Activate* and click *Ok* in the dialog.
96+
97+
Note the *IDCS URL*, this is the URL you see in your browser URL bar, copy the IDCS url ( For example: https://idcs-xxxxxxxxxxx.identity.oraclecloud.com/ ), client-id, client-secret and scope these values are provided to the Basicauth function.
98+
99+
100+
101+
## Review and customize the function
102+
Review the following files in the current folder:
103+
- [pom.xml](./pom.xml) specifies all the dependencies for your function
104+
- [func.yaml](./func.yaml) that contains metadata about your function and declares properties
105+
- [src/main/java/com/example/fn/BasicAuth.java](./src/main/java/com/example/fn/BasicAuth.java) which contains the Java code
106+
107+
The name of your function *basicauth* is specified in [func.yaml](./func.yaml).
108+
109+
set the following variable in "src/main/java/com/example/utils/ResourceServerConfig.java" to the values noted while configuring IDCS.
110+
```
111+
public static final String CLIENT_ID = "xxxxxxxxxxx";
112+
public static final String CLIENT_SECRET = "xxxxxxxxx";
113+
public static final String IDCS_URL = "https://idcs-xxxxxxxx.identity.oraclecloud.com";
114+
115+
//INFORMATION ABOUT THE TARGET APPLICATION
116+
public static final String SCOPE_AUD = "display-httprequest-infodisplay-httprequest-info";
117+
```
118+
119+
120+
## Deploy the basicauth function
121+
In Cloud Shell, run the *fn deploy* command to build the function and its dependencies as a Docker image,
122+
push the image to OCIR, and deploy the function to Oracle Functions in your application.
123+
124+
![user input icon](./images/userinput.png)
125+
```
126+
fn -v deploy --app <app-name>
127+
```
128+
## Invoke the basicauth function in cloud shell
129+
In Cloud Shell, run *fn invoke* command to invoke the deployed function, returns active status as true if the token is valid or else returns false.
130+
131+
![user input icon](./images/userinput.png)
132+
```
133+
echo -n '{"type":"TOKEN", "token":"Basic aW5jaGFyYS5zaGFtYW5uYUBvcmFj....."}' | fn invoke <app-name> <func-name>
134+
```
135+
136+
## Create the API Gateway
137+
The functions is meant to be invoked through API Gateway.
138+
139+
![user input icon](./images/userinput.png)
140+
141+
On the OCI console, navigate to *Developer Services* > *API Gateway*. Click on *Create Gateway*. Provide a name, set the type to "Public", select a compartment, a VCN, a public subnet, and click *Create*.
142+
143+
![APIGW create](./images/apigw-create.png)
144+
145+
Once created, click on your gateway. Under *Resources*, select *Deployments* and click *Create Deployment*.
146+
147+
* Provide a name, a path prefix ("/basicauth" for example).
148+
* Under *API Request Policies* Add Authentication
149+
* Authentication Type: *Custom*
150+
* Choose the application and the basicauth function
151+
* For "Authentication token", select *Header*
152+
* For the "Header Name", enter "Autorization"
153+
154+
Click *Save Changes* when you are finished
155+
![APIGW deployment create](./images/apigw-deployment-create.png)
156+
157+
Click *Next*. Provide a name to the route ("/hello" for example), select methods eg: "GET", select *HTTP-URL* for your back-end.
158+
159+
![APIGW deployment create](./images/apigw-deployment-create-route.png)
160+
161+
Click *Next* and finally, click *Save Changes*.
162+
163+
Note the endpoint of your API Gateway deployment.
164+
165+
![APIGW deployment endpoint](./images/apigw-deployment-endpoint.png)
166+
167+
168+
## Invoke the Deployment endpoint
169+
The function validates if the user information is valid.
170+
171+
![user input icon](./images/userinput.png)
172+
173+
Use the curl command to make the HTTP request
174+
```
175+
curl -i -u "<username>:<password>" https://d6xxxxxxxxk64.apigateway.us-ashburn-1.oci.customer-oci.com/basicauth/hello
176+
```
177+
If the user is valid gateway will make a call to backend with HTTP200 else
178+
The gateway will reject the request with an HTTP401.
179+
180+
181+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
schema_version: 20180708
2+
name: basicauth
3+
version: 0.0.6
4+
runtime: java
5+
build_image: fnproject/fn-java-fdk-build:jdk11-1.0.146
6+
run_image: fnproject/fn-java-fdk:jre11-1.0.146
7+
cmd: com.example.fn.BasicAuth::handleRequest
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Loading
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<properties>
7+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
8+
<fdk.version>1.0.146</fdk.version>
9+
</properties>
10+
<groupId>com.example.fn</groupId>
11+
<artifactId>basicAuth</artifactId>
12+
<version>1.0.0</version>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>com.fnproject.fn</groupId>
17+
<artifactId>api</artifactId>
18+
<version>${fdk.version}</version>
19+
</dependency>
20+
<dependency>
21+
<groupId>com.fnproject.fn</groupId>
22+
<artifactId>testing-core</artifactId>
23+
<version>${fdk.version}</version>
24+
<scope>test</scope>
25+
</dependency>
26+
<dependency>
27+
<groupId>com.fnproject.fn</groupId>
28+
<artifactId>testing-junit4</artifactId>
29+
<version>${fdk.version}</version>
30+
<scope>test</scope>
31+
</dependency>
32+
<dependency>
33+
<groupId>org.json</groupId>
34+
<artifactId>json</artifactId>
35+
<version>20180130</version>
36+
</dependency>
37+
<dependency>
38+
<groupId>junit</groupId>
39+
<artifactId>junit</artifactId>
40+
<version>4.12</version>
41+
<scope>test</scope>
42+
</dependency>
43+
</dependencies>
44+
45+
<build>
46+
<plugins>
47+
<plugin>
48+
<groupId>org.apache.maven.plugins</groupId>
49+
<artifactId>maven-compiler-plugin</artifactId>
50+
<version>3.3</version>
51+
<configuration>
52+
<source>11</source>
53+
<target>11</target>
54+
</configuration>
55+
</plugin>
56+
<plugin>
57+
<groupId>org.apache.maven.plugins</groupId>
58+
<artifactId>maven-surefire-plugin</artifactId>
59+
<version>2.22.1</version>
60+
<configuration>
61+
<useSystemClassLoader>false</useSystemClassLoader>
62+
</configuration>
63+
</plugin>
64+
</plugins>
65+
</build>
66+
</project>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package com.example.fn;
2+
3+
import java.net.http.HttpClient;
4+
import java.net.http.HttpRequest;
5+
import java.net.http.HttpResponse;
6+
import java.net.URI;
7+
8+
import java.io.IOException;
9+
import java.io.UnsupportedEncodingException;
10+
import java.lang.InterruptedException;
11+
import org.json.JSONObject;
12+
import java.util.*;
13+
14+
public class BasicAuth {
15+
public static class Input {
16+
public String type;
17+
public String token;
18+
}
19+
20+
public static class Result {
21+
public boolean active = false;
22+
public String principal;
23+
public String[] scope;
24+
public String expiresAt;
25+
}
26+
27+
private static final String TOKEN_PREFIX = "Basic ";
28+
private static final String IDCS_URL = ResourceServerConfig.IDCS_URL;
29+
30+
public String[] getUserDetailsFromToken(String token) {
31+
String data = token.substring(TOKEN_PREFIX.length());
32+
String[] user = new String(Base64.getDecoder().decode(data)).split(":", 2);
33+
return user;
34+
}
35+
36+
public Result handleRequest(Input input) throws UnsupportedEncodingException, InterruptedException {
37+
Result returnValue = new Result();
38+
39+
if (input.token == null || !input.token.startsWith(TOKEN_PREFIX)) {
40+
returnValue.active = false;
41+
System.out.println("Request error, missing credentials");
42+
return returnValue;
43+
}
44+
45+
String[] user = getUserDetailsFromToken(input.token);
46+
if (user.length != 2 || user[0] == null || user[0].isEmpty() || user[1] == null || user[1].isEmpty()) {
47+
System.out.println("Request error username or password missing");
48+
return returnValue;
49+
}
50+
51+
String username = user[0];
52+
String password = user[1];
53+
String clientId = ResourceServerConfig.CLIENT_ID;
54+
String clientSecret = ResourceServerConfig.CLIENT_SECRET;
55+
String authzHdrVal = clientId + ":" + clientSecret;
56+
String idcsScope = ResourceServerConfig.SCOPE_AUD;
57+
58+
String reqBody = "grant_type=password" +
59+
"&username=" + username +
60+
"&password=" + password +
61+
"&scope=" + idcsScope;
62+
63+
try {
64+
HttpClient client = HttpClient.newHttpClient();
65+
66+
HttpRequest request = HttpRequest.newBuilder()
67+
.uri(URI.create(IDCS_URL))
68+
.header("Content-Type", "application/x-www-form-urlencoded")
69+
.header("Authorization", "Basic " + Base64.getEncoder().encodeToString(authzHdrVal.getBytes("UTF-8")))
70+
.POST(HttpRequest.BodyPublishers.ofString(reqBody))
71+
.build();
72+
73+
HttpResponse<String> response = client.send( request,
74+
HttpResponse.BodyHandlers.ofString() );
75+
76+
77+
if (response.statusCode() == 200) {
78+
79+
String responseString = (String) response.body();
80+
String[] chunks = responseString.split("\\.");
81+
String respjson = new String(Base64.getUrlDecoder().decode(chunks[1]));
82+
JSONObject payload = new JSONObject(respjson);
83+
Date expTime = new Date(payload.getLong("exp")*1000);
84+
85+
returnValue.principal = payload.getString("sub");
86+
returnValue.scope = payload.getString("scope").split(" ");
87+
returnValue.expiresAt = expTime.toString();
88+
returnValue.active = true;
89+
System.out.println("Authentication successful");
90+
}
91+
} catch (IOException e) {
92+
e.printStackTrace();
93+
}
94+
return returnValue;
95+
}
96+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.example.fn;
2+
3+
public class ResourceServerConfig {
4+
5+
//YOUR IDENTITY DOMAIN AND APPLICATION CREDENTIALS
6+
public static final String CLIENT_ID = "";
7+
public static final String CLIENT_SECRET = "";
8+
public static final String IDCS_URL = "";
9+
10+
//INFORMATION ABOUT THE TARGET APPLICATION (For eg : display-httprequest-infodisplay-httprequest-info)
11+
public static final String SCOPE_AUD = "";
12+
13+
//TEST CLIENT CREDENTIALS
14+
public static final String TEST_CLIENT_ID = "";
15+
public static final String TEST_CLIENT_SECRET = "";
16+
public static final String TEST_CLIENT_SCOPE = "";
17+
public static final String TOKEN = ""; // EG : "aW5jaGFyvcmFjbGUuY29tFhU"
18+
19+
20+
21+
}

0 commit comments

Comments
 (0)