Skip to content

Commit f0d8be5

Browse files
authored
Merge pull request #2125 from oracle-devrel/java-basic-authorizer-apigw-fn-example
java-basic-authorizer-apigw-fn-example added
2 parents efc44cd + a3fa2d2 commit f0d8be5

File tree

12 files changed

+370
-3
lines changed

12 files changed

+370
-3
lines changed
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<!--
2+
Copyright (c) 2025 Oracle and/or its affiliates.
3+
4+
The Universal Permissive License (UPL), Version 1.0
5+
6+
Subject to the condition set forth below, permission is hereby granted to any
7+
person obtaining a copy of this software, associated documentation and/or data
8+
(collectively the "Software"), free of charge and under any and all copyright
9+
rights in the Software, and any and all patent rights owned or freely
10+
licensable by each licensor hereunder covering either (i) the unmodified
11+
Software as contributed to or provided by such licensor, or (ii) the Larger
12+
Works (as defined below), to deal in both
13+
14+
(a) the Software, and
15+
(b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
16+
one is included with the Software (each a "Larger Work" to which the Software
17+
is contributed by such licensors),
18+
19+
without restriction, including without limitation the rights to copy, create
20+
derivative works of, display, perform, and distribute the Software and make,
21+
use, sell, offer for sale, import, export, have made, and have sold the
22+
Software and the Larger Work(s), and to sublicense the foregoing rights on
23+
either these or other terms.
24+
25+
This license is subject to the following condition:
26+
The above copyright notice and either this complete permission notice or at
27+
a minimum a reference to the UPL must be included in all copies or
28+
substantial portions of the Software.
29+
30+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
31+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
32+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
33+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
34+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
36+
SOFTWARE.
37+
-->
38+
39+
# API Gateway basic-auth Authorizer Function example
40+
41+
Reviewed: 13.10.2025
42+
43+
# When to use this asset?
44+
45+
Anyone who wants to implement an API Gateway authorizer Function for HTTP basic-auth
46+
47+
# Author
48+
<a href="https://github.com/mikarinneoracle">mikarinneoracle</a>
49+
50+
# How to use this asset?
51+
52+
Build and deploy both functions <code>fn-authorizer-auth-basic</code> and <code>fn-authorizer-auth-basic-test</code> under <code>/files</code> to a Function Application in OCI.
53+
<p>
54+
Create a config for the <code>basicauthorizerfnjava</code> Function:
55+
<p>
56+
<img src="files/authorizer-config.png" width="800" />
57+
<p>
58+
Config above contains two comma-separated base64 encoded key-value pairs for basic-auth authentication:
59+
<pre>
60+
aGk6aGk=,Zm9vOmJhcg==
61+
</pre>
62+
63+
<b>aGk6aGk=</b> is <b>hi:hi</b> => username is <b>hi</b>, password <b>hi</b>
64+
<p>
65+
<b>aGk6aGk=</b> is <b>foo:bar</b> => username is <b>foo</b>, password <b>bar</b>
66+
<p>
67+
(You can modify the config by adding new pairs as you like and remove the existing ones)
68+
<p>
69+
After deploying the functions add an API Gateway instance and configure the Functions:
70+
71+
<p>
72+
<img src="files/authorizer-function.png" width="800" />
73+
<p>
74+
75+
Settings for the <b><i>Single argument authorizer function</i></b>:
76+
<p>
77+
Token location: <b>Header</b>
78+
<br>
79+
Token header name: <b>Authentication</b>
80+
81+
<p>
82+
Configure the route for the backend function:
83+
84+
<p>
85+
<img src="files/backend-function.png" width="800" />
86+
<p>
87+
88+
Configure Route Request Policies Header transformations:
89+
90+
<img src="files/header-transformations.png" width="800" />
91+
<p>
92+
93+
Settings for the <b><i>Header transformations</i></b>:
94+
<p>
95+
Behavior: <b>Overwrite</b>
96+
<br>
97+
Header name: <b>username</b>
98+
<br>
99+
Values: <b>${request.auth[username]}</b>
100+
<p>
101+
102+
Test by accessing the API Gateway url from the browse and after the functions have been loaded you should see a basic-auth authentication request popping up. Enter <code>foo</code> and <code>bar</code> and after accepting the backend function should return:
103+
104+
<pre>
105+
Username: foo
106+
</pre>
107+
108+
# Useful Links
109+
110+
- [OCI Functions](https://docs.oracle.com/en-us/iaas/Content/Functions/Concepts/functionsoverview.htm)
111+
- Learn how the Functions service lets you create, run, and scale business logic without managing any infrastructure
112+
- [Oracle](https://www.oracle.com/)
113+
- Oracle Website
114+
115+
### License
116+
117+
Copyright (c) 2025 Oracle and/or its affiliates.
118+
119+
Licensed under the Universal Permissive License (UPL), Version 1.0.
120+
121+
See [LICENSE](https://github.com/oracle-devrel/technology-engineering/blob/main/LICENSE) for more details.
347 KB
Loading
288 KB
Loading
167 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
schema_version: 20180708
2+
name: java-hello-world-basic-auth-test
3+
version: 0.0.1
4+
runtime: java
5+
build_image: fnproject/fn-java-fdk-build:jdk17-1.0.207
6+
run_image: fnproject/fn-java-fdk:jre17-1.0.207
7+
cmd: com.example.fn.HelloFunction::handleRequest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.207</fdk.version>
9+
</properties>
10+
<groupId>com.example.fn</groupId>
11+
<artifactId>hello</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>junit</groupId>
34+
<artifactId>junit</artifactId>
35+
<version>4.12</version>
36+
<scope>test</scope>
37+
</dependency>
38+
</dependencies>
39+
40+
<build>
41+
<plugins>
42+
<plugin>
43+
<groupId>org.apache.maven.plugins</groupId>
44+
<artifactId>maven-compiler-plugin</artifactId>
45+
<version>3.3</version>
46+
<configuration>
47+
<source>17</source>
48+
<target>17</target>
49+
</configuration>
50+
</plugin>
51+
<plugin>
52+
<groupId>org.apache.maven.plugins</groupId>
53+
<artifactId>maven-surefire-plugin</artifactId>
54+
<version>2.22.1</version>
55+
<configuration>
56+
<useSystemClassLoader>false</useSystemClassLoader>
57+
</configuration>
58+
</plugin>
59+
</plugins>
60+
</build>
61+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.example.fn;
2+
3+
import com.fnproject.fn.api.RuntimeContext;
4+
import com.fnproject.fn.api.httpgateway.HTTPGatewayContext;
5+
import com.fnproject.fn.api.InputEvent;
6+
7+
public class HelloFunction {
8+
9+
public String handleRequest(final HTTPGatewayContext hctx, final InputEvent input) {
10+
11+
// Use header transformation in APIGW Route to get username in headers from authorizer
12+
// Overwrite username ${request.auth[username]}
13+
String username = hctx.getHeaders().get("username").orElse("");
14+
return "Username: " + username;
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
schema_version: 20180708
2+
name: basicauthorizerfnjava
3+
version: 0.0.2
4+
runtime: java
5+
build_image: fnproject/fn-java-fdk-build:jdk17-1.0.207
6+
run_image: fnproject/fn-java-fdk:jre17-1.0.207
7+
cmd: com.example.fn.HelloFunction::handleRequest
8+
memory: 1024
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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.207</fdk.version>
9+
</properties>
10+
<groupId>com.example.fn</groupId>
11+
<artifactId>hello</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>junit</groupId>
34+
<artifactId>junit</artifactId>
35+
<version>4.12</version>
36+
<scope>test</scope>
37+
</dependency>
38+
</dependencies>
39+
40+
<build>
41+
<plugins>
42+
<plugin>
43+
<groupId>org.apache.maven.plugins</groupId>
44+
<artifactId>maven-compiler-plugin</artifactId>
45+
<version>3.3</version>
46+
<configuration>
47+
<source>17</source>
48+
<target>17</target>
49+
</configuration>
50+
</plugin>
51+
<plugin>
52+
<groupId>org.apache.maven.plugins</groupId>
53+
<artifactId>maven-surefire-plugin</artifactId>
54+
<version>2.22.1</version>
55+
<configuration>
56+
<useSystemClassLoader>false</useSystemClassLoader>
57+
</configuration>
58+
</plugin>
59+
</plugins>
60+
</build>
61+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.example.fn;
2+
3+
import com.fnproject.fn.api.FnConfiguration;
4+
import com.fnproject.fn.api.Headers;
5+
import com.fnproject.fn.api.InputEvent;
6+
7+
import com.fnproject.fn.api.RuntimeContext;
8+
import com.fnproject.fn.api.httpgateway.HTTPGatewayContext;
9+
10+
import java.io.*;
11+
import java.nio.file.Files;
12+
import java.nio.file.Paths;
13+
import java.time.LocalDateTime;
14+
import java.time.format.DateTimeFormatter;
15+
import java.util.Arrays;
16+
import java.util.Base64;
17+
import java.util.List;
18+
import java.util.stream.Collectors;
19+
20+
public class HelloFunction {
21+
22+
String authConfig = "";
23+
24+
@FnConfiguration
25+
public void setUp(RuntimeContext ctx) throws Exception {
26+
authConfig = ctx.getConfigurationByKey("config").orElse(System.getenv().getOrDefault("config", ""));
27+
}
28+
29+
public String handleRequest(final HTTPGatewayContext hctx, final InputEvent input) {
30+
31+
boolean IS_FOUND = false;
32+
String ret = "";
33+
String username = "";
34+
35+
String body = input.consumeBody((InputStream is) -> {
36+
try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
37+
return reader.lines().collect(Collectors.joining());
38+
} catch (IOException e) {
39+
throw new RuntimeException(e);
40+
}
41+
});
42+
System.out.println("Body: " + body);
43+
44+
String[] configTokens = authConfig.split(",");
45+
List<String> tokenizedConfig = Arrays.stream(configTokens).map(String::trim).collect(Collectors.toList());
46+
47+
if(body.length() > 0) {
48+
String[] bodyTokens = body.split(",");
49+
List<String> tokenizedBody = Arrays.stream(bodyTokens).map(String::trim).collect(Collectors.toList());
50+
51+
for (String configToken : tokenizedConfig) {
52+
for (String token : tokenizedBody) {
53+
if (token.indexOf("Basic ") > -1 && configToken.length() > 0) {
54+
String auth_token = token.substring(token.indexOf("Basic ") + 6, token.indexOf("\"}"));
55+
if (auth_token.equals(configToken)) {
56+
System.out.println("AUTH SUCCESS " + auth_token + " == " + configToken);
57+
byte[] decodedBytes = Base64.getDecoder().decode(auth_token);
58+
String decodedString = new String(decodedBytes);
59+
String[] decodedTokens = decodedString.split(":");
60+
username = decodedTokens[0];
61+
IS_FOUND = true;
62+
} else {
63+
System.out.println("AUTH NO MATCH " + auth_token + " <> " + configToken);
64+
}
65+
}
66+
}
67+
}
68+
}
69+
70+
// Use header transformation in APIGW Route to get username in headers from this response
71+
// in the target function:
72+
// Overwrite username ${request.auth[username]}
73+
if(IS_FOUND) {
74+
LocalDateTime dateTime = LocalDateTime.now().plusDays(1);
75+
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'+00:00'");
76+
String expiryDate = dateTime.format(formatter);
77+
ret = "{ " +
78+
"\"active\": true," +
79+
"\"principal\": \"myprincipal\"," +
80+
"\"scope\": [\"fnbasicauthtest\"]," +
81+
"\"expiresAt\": \"" + expiryDate + "\"," +
82+
"\"context\": { \"username\": \"" + username + "\" }" +
83+
" }";
84+
} else {
85+
ret = "{ " +
86+
"\"active\": false," +
87+
"\"wwwAuthenticate\": \"Basic realm=\\\"fnbasicauthtest.io\\\"\"" +
88+
" }";
89+
}
90+
System.out.println(ret);
91+
return ret;
92+
}
93+
}

0 commit comments

Comments
 (0)