feat(auth) Update auth cookies to have same-site none for chrome extension (#6976)

This commit is contained in:
Chris Collins 2023-01-11 17:31:20 -05:00 committed by GitHub
parent dcf389d35f
commit 0337110928
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 9 deletions

View File

@ -4,6 +4,7 @@ import com.linkedin.common.urn.CorpuserUrn;
import lombok.extern.slf4j.Slf4j;
import play.mvc.Http;
import javax.annotation.Nonnull;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
@ -44,6 +45,11 @@ public class AuthUtils {
public static final Integer DEFAULT_SESSION_TTL_HOURS = 720;
public static final CorpuserUrn DEFAULT_ACTOR_URN = new CorpuserUrn("datahub");
public static final String AUTH_COOKIE_SAME_SITE = "play.http.session.sameSite";
public static final String DEFAULT_AUTH_COOKIE_SAME_SITE = "LAX";
public static final String AUTH_COOKIE_SECURE = "play.http.session.secure";
public static final boolean DEFAULT_AUTH_COOKIE_SECURE = false;
public static final String LOGIN_ROUTE = "/login";
public static final String USER_NAME = "username";
public static final String PASSWORD = "password";
@ -96,11 +102,18 @@ public class AuthUtils {
* @param actorUrn the urn of the authenticated actor, e.g. "urn:li:corpuser:datahub"
* @param ttlInHours the number of hours until the actor cookie expires after being set
*/
public static Http.Cookie createActorCookie(final String actorUrn, final Integer ttlInHours) {
public static Http.Cookie createActorCookie(
@Nonnull final String actorUrn,
@Nonnull final Integer ttlInHours,
@Nonnull final String sameSite,
final boolean isSecure
) {
return Http.Cookie.builder(ACTOR, actorUrn)
.withHttpOnly(false)
.withMaxAge(Duration.of(ttlInHours, ChronoUnit.HOURS))
.build();
.withHttpOnly(false)
.withMaxAge(Duration.of(ttlInHours, ChronoUnit.HOURS))
.withSameSite(convertSameSiteValue(sameSite))
.withSecure(isSecure)
.build();
}
public static Map<String, String> createSessionMap(final String userUrnStr, final String accessToken) {
@ -112,4 +125,13 @@ public class AuthUtils {
private AuthUtils() { }
private static Http.Cookie.SameSite convertSameSiteValue(@Nonnull final String sameSiteValue) {
try {
return Http.Cookie.SameSite.valueOf(sameSiteValue);
} catch (IllegalArgumentException e) {
log.warn(String.format("Invalid AUTH_COOKIE_SAME_SITE value: %s. Using LAX instead.", sameSiteValue), e);
return Http.Cookie.SameSite.LAX;
}
}
}

View File

@ -28,6 +28,8 @@ public class SsoConfigs {
private final String _authSuccessRedirectPath;
private final Integer _sessionTtlInHours;
private final Boolean _oidcEnabled;
private final String _authCookieSameSite;
private final Boolean _authCookieSecure;
public SsoConfigs(final com.typesafe.config.Config configs) {
_authBaseUrl = getRequired(configs, AUTH_BASE_URL_CONFIG_PATH);
@ -46,6 +48,14 @@ public class SsoConfigs {
_oidcEnabled = configs.hasPath(OIDC_ENABLED_CONFIG_PATH)
&& Boolean.TRUE.equals(
Boolean.parseBoolean(configs.getString(OIDC_ENABLED_CONFIG_PATH)));
_authCookieSameSite = getOptional(
configs,
AUTH_COOKIE_SAME_SITE,
DEFAULT_AUTH_COOKIE_SAME_SITE);
_authCookieSecure = Boolean.parseBoolean(getOptional(
configs,
AUTH_COOKIE_SECURE,
String.valueOf(DEFAULT_AUTH_COOKIE_SECURE)));
}
public String getAuthBaseUrl() {
@ -64,6 +74,14 @@ public class SsoConfigs {
return _sessionTtlInHours;
}
public String getAuthCookieSameSite() {
return _authCookieSameSite;
}
public boolean getAuthCookieSecure() {
return _authCookieSecure;
}
public Boolean isOidcEnabled() {
return _oidcEnabled;
}

View File

@ -154,7 +154,14 @@ public class OidcCallbackLogic extends DefaultCallbackLogic<Result, PlayWebConte
final String accessToken = _authClient.generateSessionTokenForUser(corpUserUrn.getId());
return result
.withSession(createSessionMap(corpUserUrn.toString(), accessToken))
.withCookies(createActorCookie(corpUserUrn.toString(), oidcConfigs.getSessionTtlInHours()));
.withCookies(
createActorCookie(
corpUserUrn.toString(),
oidcConfigs.getSessionTtlInHours(),
oidcConfigs.getAuthCookieSameSite(),
oidcConfigs.getAuthCookieSecure()
)
);
}
return internalServerError(
"Failed to authenticate current user. Cannot find valid identity provider profile in session.");

View File

@ -32,7 +32,11 @@ import play.mvc.Result;
import play.mvc.Results;
import security.AuthenticationManager;
import static auth.AuthUtils.AUTH_COOKIE_SAME_SITE;
import static auth.AuthUtils.AUTH_COOKIE_SECURE;
import static auth.AuthUtils.DEFAULT_ACTOR_URN;
import static auth.AuthUtils.DEFAULT_AUTH_COOKIE_SAME_SITE;
import static auth.AuthUtils.DEFAULT_AUTH_COOKIE_SECURE;
import static auth.AuthUtils.DEFAULT_SESSION_TTL_HOURS;
import static auth.AuthUtils.EMAIL;
import static auth.AuthUtils.FULL_NAME;
@ -115,10 +119,15 @@ public class AuthenticationController extends Controller {
// 3. If no auth enabled, fallback to using default user account & redirect.
// Generate GMS session token, TODO:
final String accessToken = _authClient.generateSessionTokenForUser(DEFAULT_ACTOR_URN.getId());
int ttlInHours = _configs.hasPath(SESSION_TTL_CONFIG_PATH) ? _configs.getInt(SESSION_TTL_CONFIG_PATH)
: DEFAULT_SESSION_TTL_HOURS;
String authCookieSameSite = _configs.hasPath(AUTH_COOKIE_SAME_SITE) ? _configs.getString(AUTH_COOKIE_SAME_SITE)
: DEFAULT_AUTH_COOKIE_SAME_SITE;
boolean authCookieSecure = _configs.hasPath(AUTH_COOKIE_SECURE) ? _configs.getBoolean(AUTH_COOKIE_SECURE)
: DEFAULT_AUTH_COOKIE_SECURE;
return Results.redirect(redirectPath).withSession(createSessionMap(DEFAULT_ACTOR_URN.toString(), accessToken))
.withCookies(createActorCookie(DEFAULT_ACTOR_URN.toString(),
_configs.hasPath(SESSION_TTL_CONFIG_PATH) ? _configs.getInt(SESSION_TTL_CONFIG_PATH)
: DEFAULT_SESSION_TTL_HOURS));
.withCookies(createActorCookie(DEFAULT_ACTOR_URN.toString(), ttlInHours, authCookieSameSite, authCookieSecure));
}
/**
@ -329,7 +338,12 @@ public class AuthenticationController extends Controller {
private Result createSession(String userUrnString, String accessToken) {
int ttlInHours = _configs.hasPath(SESSION_TTL_CONFIG_PATH) ? _configs.getInt(SESSION_TTL_CONFIG_PATH)
: DEFAULT_SESSION_TTL_HOURS;
String authCookieSameSite = _configs.hasPath(AUTH_COOKIE_SAME_SITE) ? _configs.getString(AUTH_COOKIE_SAME_SITE)
: DEFAULT_AUTH_COOKIE_SAME_SITE;
boolean authCookieSecure = _configs.hasPath(AUTH_COOKIE_SECURE) ? _configs.getBoolean(AUTH_COOKIE_SECURE)
: DEFAULT_AUTH_COOKIE_SECURE;
return Results.ok().withSession(createSessionMap(userUrnString, accessToken))
.withCookies(createActorCookie(userUrnString, ttlInHours));
.withCookies(createActorCookie(userUrnString, ttlInHours, authCookieSameSite, authCookieSecure));
}
}

View File

@ -36,6 +36,15 @@ play.http.server.akka.max-header-count = ${?DATAHUB_AKKA_MAX_HEADER_COUNT}
play.server.akka.max-header-size = 8k
play.server.akka.max-header-size = ${?DATAHUB_AKKA_MAX_HEADER_VALUE_LENGTH}
# Update AUTH_COOKIE_SAME_SITE and AUTH_COOKIE_SECURE in order to change how authentication cookies
# are configured. If you wish cookies to be sent in first and third party contexts, set
# AUTH_COOKIE_SAME_SITE = "NONE" and AUTH_COOKIE_SECURE = true. If AUTH_COOKIE_SAME_SITE is "NONE",
# AUTH_COOKIE_SECURE must be set to true.
play.http.session.sameSite = "LAX"
play.http.session.sameSite = ${?AUTH_COOKIE_SAME_SITE}
play.http.session.secure = false
play.http.session.secure = ${?AUTH_COOKIE_SECURE}
play.filters {
enabled = [
play.filters.gzip.GzipFilter

View File

@ -60,6 +60,7 @@ dependencies {
implementation externalDependency.kafkaClients
implementation externalDependency.awsMskIamAuth
testImplementation 'org.seleniumhq.selenium:htmlunit-driver:2.67.0'
testImplementation externalDependency.mockito
testImplementation externalDependency.playTest
testImplementation 'org.awaitility:awaitility:4.2.0'