Skip to content

Commit a77fee7

Browse files
authored
IAP sample update : new JWT header (#757)
1 parent bd564e8 commit a77fee7

File tree

5 files changed

+53
-18
lines changed

5 files changed

+53
-18
lines changed

appengine/iap/src/main/java/com/example/appengine/iap/JwtServlet.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
@SuppressWarnings("serial")
2626
public class JwtServlet extends HttpServlet {
2727

28-
private static final String IAP_JWT_HEADER = "x-goog-authenticated-user-jwt";
28+
private static final String IAP_JWT_HEADER = "x-goog-iap-jwt-assertion";
2929

3030
@Override
3131
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

iap/README.md

+17-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,25 @@ verify the JWT token in an incoming request to an IAP protected resource.
2323
## Testing
2424
- Deploy the [demo app engine application](../appengine/iap/README.md). This application will return the JWT token to an authorized incoming request.
2525
It will be used to test both the authorization of an incoming request to an IAP protected resource and the JWT token returned from IAP.
26+
2627
- [Enable](https://cloud.google.com/iap/docs/app-engine-quickstart) Identity-Aware Proxy on the App Engine app.
28+
2729
- Add the service account email to the Identity-Aware Proxy access list for the project.
28-
- Set the environment variable `IAP_PROTECTED_URL` to point to `https://your-project-id.appspot.com`
29-
- Set the environment variable `IAP_CLIENT_ID` to point to the [OAuth 2.0 Client ID](https://console.cloud.google.com/apis/credentials) of your IAP protected App Engine Application.
30+
31+
- Set the following environment variables to test sending a request to an IAP protected resource:
32+
- `IAP_PROTECTED_URL` : URL of your IAP protected resource . eg. `https://your-project-id.appspot.com`
33+
34+
- `IAP_CLIENT_ID` to point to the [OAuth 2.0 Client ID](https://console.cloud.google.com/apis/credentials) of your IAP protected App Engine Application.
35+
36+
- Set the following environment variables to test verifying a JWT issued for an App Engine protected application:
37+
- `GOOGLE_CLOUD_PROJECT`: Google Cloud Project ID
38+
39+
- `IAP_PROJECT_NUMBER` : [Project number](https://console.cloud.google.com/home/dashboard) of the IAP protected resource.
40+
Also available via `gcloud` using:
41+
```
42+
gcloud projects describe PROJECT_ID
43+
```
44+
3045
- Run the integration test:
3146
```
3247
mvn -Dtest=com.example.iap.BuildAndVerifyIapRequestIT verify

iap/src/main/java/com/example/iap/VerifyIapRequestHeader.java

+22-13
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
import java.io.IOException;
3333
import java.io.StringReader;
34-
import java.net.URL;
3534
import java.security.Key;
3635
import java.security.KeyFactory;
3736
import java.security.NoSuchAlgorithmException;
@@ -100,33 +99,43 @@ private Key resolveSigningKey(JwsHeader header) {
10099
}
101100
};
102101

103-
private static String getBaseUrl(URL url) throws Exception {
104-
String urlFilePath = url.getFile();
105-
int pathDelim = urlFilePath.lastIndexOf('/');
106-
String path = (pathDelim > 0) ? urlFilePath.substring(0, pathDelim) : "";
107-
return (url.getProtocol() + "://" + url.getHost() + path).trim();
102+
// Verify jwt tokens addressed to IAP protected resources on App Engine.
103+
// The project *number* for your Google Cloud project available via 'gcloud projects describe $PROJECT_ID'
104+
// or in the Project Info card in Cloud Console.
105+
// projectId is The project *ID* for your Google Cloud Project.
106+
Jwt verifyJWTTokenForAppEngine(HttpRequest request, long projectNumber, String projectId) throws Exception {
107+
// Check for iap jwt header in incoming request
108+
String jwtToken =
109+
request.getHeaders().getFirstHeaderStringValue("x-goog-iap-jwt-assertion");
110+
if (jwtToken == null) {
111+
return null;
112+
}
113+
return verifyJWTToken(jwtToken, String.format("/projects/%s/apps/%s",
114+
Long.toUnsignedString(projectNumber),
115+
projectId));
108116
}
109117

110-
Jwt verifyJWTToken(HttpRequest request) throws Exception {
118+
Jwt verifyJWTTokenForComputeEngine(HttpRequest request, long projectNumber, long backendServiceId) throws Exception {
111119
// Check for iap jwt header in incoming request
112120
String jwtToken =
113-
request.getHeaders().getFirstHeaderStringValue("x-goog-authenticated-user-jwt");
121+
request.getHeaders().getFirstHeaderStringValue("x-goog-iap-jwt-assertion");
114122
if (jwtToken == null) {
115123
return null;
116124
}
117-
String baseUrl = getBaseUrl(request.getUrl().toURL());
118-
return verifyJWTToken(jwtToken, baseUrl);
125+
return verifyJWTToken(jwtToken, String.format("/projects/%s/global/backendServices/%s",
126+
Long.toUnsignedString(projectNumber),
127+
Long.toUnsignedString(backendServiceId)));
119128
}
120-
121-
Jwt verifyJWTToken(String jwtToken, String baseUrl) throws Exception {
129+
130+
Jwt verifyJWTToken(String jwtToken, String expectedAudience) throws Exception {
122131
// Time constraints are automatically checked, use setAllowedClockSkewSeconds
123132
// to specify a leeway window
124133
// The token was issued in a past date "iat" < TODAY
125134
// The token hasn't expired yet "exp" > TODAY
126135
Jwt jwt =
127136
Jwts.parser()
128137
.setSigningKeyResolver(resolver)
129-
.requireAudience(baseUrl)
138+
.requireAudience(expectedAudience)
130139
.requireIssuer(IAP_ISSUER_URL)
131140
.parse(jwtToken);
132141
DefaultClaims claims = (DefaultClaims) jwt.getBody();

iap/src/test/java/com/example/iap/BuildAndVerifyIapRequestIT.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.junit.Assert.assertNotNull;
1919

2020
import com.google.api.client.http.GenericUrl;
21+
import com.google.api.client.http.HttpHeaders;
2122
import com.google.api.client.http.HttpRequest;
2223
import com.google.api.client.http.HttpResponse;
2324
import com.google.api.client.http.HttpResponseException;
@@ -35,6 +36,8 @@ public class BuildAndVerifyIapRequestIT {
3536

3637
private String iapProtectedUrl = System.getenv("IAP_PROTECTED_URL");
3738
private String iapClientId = System.getenv("IAP_CLIENT_ID");
39+
private Long projectNumber = Long.parseLong(System.getenv("IAP_PROJECT_NUMBER"));
40+
private String projectId = System.getenv("GOOGLE_CLOUD_PROJECT");
3841
private HttpTransport httpTransport = new NetHttpTransport();
3942
private VerifyIapRequestHeader verifyIapRequestHeader = new VerifyIapRequestHeader();
4043

@@ -68,8 +71,15 @@ public void testGenerateAndVerifyIapRequestIsSuccessful() throws Exception {
6871
String[] split = headerWithtoken.split(":");
6972
assertNotNull(split);
7073
assertEquals(split.length, 2);
71-
assertEquals(split[0].trim(), "x-goog-authenticated-user-jwt");
72-
Jwt decodedJWT = verifyIapRequestHeader.verifyJWTToken(split[1].trim(), iapProtectedUrl);
74+
assertEquals(split[0].trim(), "x-goog-iap-jwt-assertion");
75+
76+
String jwtToken = split[1].trim();
77+
HttpRequest verifyJwtRequest = httpTransport
78+
.createRequestFactory()
79+
.buildGetRequest(new GenericUrl(iapProtectedUrl)).setHeaders(
80+
new HttpHeaders().set("x-goog-iap-jwt-assertion", jwtToken));
81+
Jwt decodedJWT = verifyIapRequestHeader.verifyJWTTokenForAppEngine(
82+
verifyJwtRequest, projectNumber, projectId);
7383
assertNotNull(decodedJWT);
7484
}
7585
}

pom.xml

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
<module>compute/sendgrid</module>
7979
<module>datastore</module>
8080
<module>datastore/cloud-client</module>
81+
<module>iap</module>
8182
<module>kms</module>
8283
<module>language/analysis</module>
8384
<module>language/cloud-client</module>

0 commit comments

Comments
 (0)