Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2025 Red Hat, Inc.
* Copyright (c) 2012-2026 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
Expand Down Expand Up @@ -38,4 +38,15 @@ public interface OAuthAuthenticatorDescriptor {
void setLinks(List<Link> links);

OAuthAuthenticatorDescriptor withLinks(List<Link> links);

/**
* The OAuth App client ID. Exposed so clients can initiate flows that require the client ID
* directly (e.g. GitHub Device Authorization Flow / RFC 8628) without needing access to the raw
* Kubernetes Secret.
*/
String getClientId();

void setClientId(String clientId);

OAuthAuthenticatorDescriptor withClientId(String clientId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2025 Red Hat, Inc.
* Copyright (c) 2012-2026 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
Expand Down Expand Up @@ -206,13 +206,16 @@ public Set<OAuthAuthenticatorDescriptor> getRegisteredAuthenticators(UriInfo uri
.withRequired(true)
.withDefaultValue("federated_login")));
OAuthAuthenticator authenticator = oauth2Providers.getAuthenticator(name);
String endpointUrl =
authenticator != null
? authenticator.getEndpointUrl()
: oauth1Providers.getAuthenticator(name).getEndpointUrl();
String clientId = authenticator != null ? authenticator.getClientId() : null;
result.add(
newDto(OAuthAuthenticatorDescriptor.class)
.withName(name)
.withEndpointUrl(
authenticator != null
? authenticator.getEndpointUrl()
: oauth1Providers.getAuthenticator(name).getEndpointUrl())
.withEndpointUrl(endpointUrl)
.withClientId(clientId)
.withLinks(links));
}
return result;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2025 Red Hat, Inc.
* Copyright (c) 2012-2026 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
Expand Down Expand Up @@ -51,6 +51,7 @@ public abstract class OAuthAuthenticator {
private static final Logger LOG = LoggerFactory.getLogger(OAuthAuthenticator.class);

protected AuthorizationCodeFlow flow;
private String clientId;
protected Map<Pattern, String> redirectUrisMap;

/**
Expand Down Expand Up @@ -109,6 +110,7 @@ protected void configure(
tokenUri,
dataStoreFactory);

this.clientId = clientId;
configure(authorizationFlow, Arrays.asList(redirectUris));
}

Expand Down Expand Up @@ -390,6 +392,11 @@ public boolean invalidateToken(String token) throws IOException {
throw new UnsupportedOperationException("Should be implemented by specific provider");
}

/** Returns the OAuth App client ID (e.g. for GitHub Device Authorization Flow). */
public String getClientId() {
Comment thread
olexii4 marked this conversation as resolved.
return clientId;
}

/**
* Get endpoint URL.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2025 Red Hat, Inc.
* Copyright (c) 2012-2026 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
Expand All @@ -26,6 +26,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;

import jakarta.ws.rs.core.Response;
Expand Down Expand Up @@ -215,4 +216,48 @@ public void shouldNotEncodeRedirectUrl() throws Exception {
// then
assertEquals(callback.getLocation().toString(), "https://redirecturl.com?params=%7B%7D");
}

@Test
public void shouldIncludeClientIdForOAuth2Providers() throws Exception {
// given
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getBaseUriBuilder()).thenReturn(UriBuilder.fromUri("http://eclipse.che"));
when(oauth2Providers.getRegisteredProviderNames()).thenReturn(Set.of("github"));
when(oauth1Providers.getRegisteredProviderNames()).thenReturn(Set.of());
OAuthAuthenticator authenticator = mock(OAuthAuthenticator.class);
when(authenticator.getClientId()).thenReturn("test-client-id");
when(oauth2Providers.getAuthenticator("github")).thenReturn(authenticator);

// when
Set<OAuthAuthenticatorDescriptor> descriptors =
embeddedOAuthAPI.getRegisteredAuthenticators(uriInfo);

// then
assertEquals(descriptors.size(), 1);
OAuthAuthenticatorDescriptor descriptor = descriptors.iterator().next();
assertEquals(descriptor.getName(), "github");
assertEquals(descriptor.getClientId(), "test-client-id");
}

@Test
public void shouldHaveNullClientIdForOAuth1Providers() throws Exception {
// given
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getBaseUriBuilder()).thenReturn(UriBuilder.fromUri("http://eclipse.che"));
when(oauth2Providers.getRegisteredProviderNames()).thenReturn(Set.of());
when(oauth1Providers.getRegisteredProviderNames()).thenReturn(Set.of("bitbucket"));
org.eclipse.che.security.oauth1.OAuthAuthenticator oauth1Authenticator =
mock(org.eclipse.che.security.oauth1.OAuthAuthenticator.class);
when(oauth1Providers.getAuthenticator("bitbucket")).thenReturn(oauth1Authenticator);

// when
Set<OAuthAuthenticatorDescriptor> descriptors =
embeddedOAuthAPI.getRegisteredAuthenticators(uriInfo);

// then
assertEquals(descriptors.size(), 1);
OAuthAuthenticatorDescriptor descriptor = descriptors.iterator().next();
assertEquals(descriptor.getName(), "bitbucket");
assertNull(descriptor.getClientId());
}
}
Loading