Skip to content

Commit

Permalink
OIDC logout (#3456)
Browse files Browse the repository at this point in the history
* Support for OIDC logout when using cookies.
* Refactored OidcConfig as it was too big.
* Only require encryption for the id token when it is expected to be used.

Signed-off-by: Tomas Langer <tomas.langer@oracle.com>
  • Loading branch information
tomas-langer authored Oct 14, 2021
1 parent f34ada8 commit 40934aa
Show file tree
Hide file tree
Showing 32 changed files with 3,502 additions and 361 deletions.
58 changes: 58 additions & 0 deletions common/http/src/main/java/io/helidon/common/http/SetCookie.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class SetCookie {
private final String path;
private final boolean secure;
private final boolean httpOnly;
private final SameSite sameSite;

private SetCookie(Builder builder) {
this.name = builder.name;
Expand All @@ -50,6 +51,7 @@ private SetCookie(Builder builder) {
this.path = builder.path;
this.secure = builder.secure;
this.httpOnly = builder.httpOnly;
this.sameSite = builder.sameSite;
}

/**
Expand Down Expand Up @@ -192,6 +194,11 @@ public String toString() {
result.append(PARAM_SEPARATOR);
result.append("HttpOnly");
}
if (sameSite != null) {
result.append(PARAM_SEPARATOR);
result.append("SameSite=");
result.append(sameSite.text());
}
return result.toString();
}

Expand All @@ -207,6 +214,7 @@ public static final class Builder implements io.helidon.common.Builder<SetCookie
private String path;
private boolean secure = false;
private boolean httpOnly = false;
private SameSite sameSite;

private Builder(String name, String value) {
Objects.requireNonNull(name, "Parameter 'name' is null!");
Expand Down Expand Up @@ -317,5 +325,55 @@ public Builder httpOnly(boolean httpOnly) {
this.httpOnly = httpOnly;
return this;
}

/**
* The {@code SameSite} cookie parameter.
*
* @param sameSite same site type to use
* @return updated builder
*/
public Builder sameSite(SameSite sameSite) {
this.sameSite = sameSite;
return this;
}
}

/**
* The SameSite attribute of the Set-Cookie HTTP response header allows you to declare if your cookie should be restricted
* to a first-party or same-site context.
*/
public enum SameSite {
/**
* Cookies are not sent on normal cross-site subrequests (for example to load images or frames into a third party site)
* , but are sent when a user is navigating to the origin site (i.e., when following a link).
*
* This is the default cookie value if SameSite has not been explicitly specified in recent browser versions
*/
LAX("Lax"),
/**
* Cookies will only be sent in a first-party context and not be sent along with requests initiated by third party
* websites.
*/
STRICT("Strict"),
/**
* Cookies will be sent in all contexts, i.e. in responses to both first-party and cross-origin requests. If
* SameSite=None is set, the cookie Secure attribute must also be set (or the cookie will be blocked).
*/
NONE("None");

private final String text;

SameSite(String text) {
this.text = text;
}

/**
* Text to write to the same site cookie param.
*
* @return text to send in cookie
*/
public String text() {
return text;
}
}
}
19 changes: 19 additions & 0 deletions docs-internal/oidc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
OIDC
----

New Open ID Connect features in Helidon.

# OIDC Logout
The capability to logout requires two pieces of information:
1. The token (JWT) that we usually have in a cookie or in a header
2. The ID token, that we get when obtaining JWT using the code flow

As we need both, we need to store both of these tokens in a cookie (or get them from a header).
This also requires encrypting these tokens, as the ID token is not public information.

To achieve this, we need

1. either configuration of encryption as part of OIDC configuration,
or use `Security` instance registered in global context (and named encryption/decryption configured).
2. support for encrypted JWT and capability to encrypt existing JWT ourselves

19 changes: 9 additions & 10 deletions examples/security/idcs-login/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ Edit application.yaml for IdcsMain.java or OidcConfig variable definition for Id
1. Create two resources called `first_scope` and `second_scope`
2. Primary Audience = `http://localhost:7987/"` (ensure there is a trailing /)
3. Within **Client Configuration**
1. Register a client
2. Allowed Grant Types = Client Credentials,JWT Assertion, Refresh Token, Authorization Code
3. Check "Allow non-HTTPS URLs"
4. Set ReDirect URL to `http://localhost:7987/oidc/redirect`
5. Client Type = Confidential
6. Add all Scopes defined in the resources section
1. Register a client
2. Allowed Grant Types = Client Credentials,JWT Assertion, Refresh Token, Authorization Code
3. Check "Allow non-HTTPS URLs"
4. Set Redirect URL to `http://localhost:7987/oidc/redirect`
5. Client Type = Confidential
6. Add all Scopes defined in the resources section
7. Set allowed operations to `Introspect`
8. Set Post Logout Redirect URL to `http://localhost:7987/loggedout`

Ensure you save and *activate* the application

Expand All @@ -48,10 +50,7 @@ Try the endpoints:

3. Open http://localhost:7987/rest/profile in your browser. This should present
you with a response highlighting your logged in role (null) correctly as you are not logged in
4. Navigate to `http://localhost:7987/jersey` this should
1. Redirect you to the OIDCS login console to authenticate yourself, once done
2. Redirects you back to the application and should display your users credentials/IDCS information
5. Executing `http://localhost:7987/jersey` displays the user details from IDCS
4. Open `http://localhost:7987/oidc/logout` in your browser. This will log you out from your IDCS and Helidon sessions

## Calling Sample from Postman

Expand Down
4 changes: 4 additions & 0 deletions examples/security/idcs-login/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.config</groupId>
<artifactId>helidon-config-yaml</artifactId>
</dependency>
<dependency>
<groupId>org.jboss</groupId>
<artifactId>jandex</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2021 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import java.util.Optional;

import io.helidon.common.LogConfig;
import io.helidon.common.context.Contexts;
import io.helidon.common.http.MediaType;
import io.helidon.config.Config;
import io.helidon.security.Security;
Expand Down Expand Up @@ -57,6 +58,8 @@ public static void main(String[] args) {
Config config = buildConfig();

Security security = Security.create(config.get("security"));
// this is needed for proper encryption/decryption of cookies
Contexts.globalContext().register(security);

Routing.Builder routing = Routing.builder()
.register(WebSecurity.create(security, config.get("security")))
Expand All @@ -70,7 +73,8 @@ public static void main(String[] args) {
.flatMap(SecurityContext::user)
.map(Subject::toString)
.orElse("Security context is null"));
});
})
.get("/loggedout", (req, res) -> res.send("You have been logged out"));

theServer = WebServer.create(routing, config.get("server"));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2018, 2020 Oracle and/or its affiliates.
# Copyright (c) 2018, 2021 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -31,6 +31,7 @@ security:
- abac:
# Adds ABAC Provider - it does not require any configuration
- oidc:
audience: "https://idcs-f936fc684541496a8b004edb789d59e1.identity.pint.oc9qadev.com:443"
client-id: "${security.properties.idcs-client-id}"
client-secret: "${security.properties.idcs-client-secret}"
identity-uri: "${security.properties.idcs-uri}"
Expand All @@ -40,6 +41,8 @@ security:
frontend-uri: "${security.properties.frontend-uri}"
# support for non-public signature JWK (and maybe other IDCS specific handling)
server-type: "idcs"
logout-enabled: true
post-logout-uri: "http://localhost:7987/loggedout"
- idcs-role-mapper:
multitenant: false
oidc-config:
Expand All @@ -56,6 +59,6 @@ security:
methods: ["get"]
authenticate: true
roles-allowed: ["my_admins"]
abac:
scopes: ["first_scope", "second_scope"]
# abac:
# scopes: ["first_scope", "second_scope"]

Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$

.level=INFO
AUDIT.level=FINEST
io.helidon.security.providers.oidc.level=FINEST
io.helidon.security.level=FINEST

Loading

0 comments on commit 40934aa

Please sign in to comment.