-
-
Notifications
You must be signed in to change notification settings - Fork 661
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(auth): add support for github SSO / OAuth2 support (#1319)
- Loading branch information
Showing
11 changed files
with
243 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
|
||
# GitHub SSO / OAuth2 | ||
To enable GitHub SSO in the application, you'll first have to enable OAuth2 in micronaut: | ||
|
||
```yaml | ||
micronaut: | ||
security: | ||
enabled: true | ||
oauth2: | ||
enabled: true | ||
clients: | ||
github: | ||
client-id: "<client-id>" | ||
client-secret: "<client-secret>" | ||
scopes: | ||
- user:email | ||
- read:user | ||
authorization: | ||
url: https://github.com/login/oauth/authorize | ||
token: | ||
url: https://github.com/login/oauth/access_token | ||
auth-method: client-secret-post | ||
``` | ||
To further tell AKHQ to display GitHub SSO options on the login page and customize claim mapping, configure Oauth in the AKHQ config: | ||
```yaml | ||
akhq: | ||
security: | ||
default-group: no-roles | ||
oauth2: | ||
enabled: true | ||
providers: | ||
github: | ||
label: "Login with GitHub" | ||
username-field: login | ||
users: | ||
- username: franz | ||
groups: | ||
# the corresponding akhq groups (eg. topic-reader/writer or akhq default groups like admin/reader/no-role) | ||
- topic-reader | ||
- topic-writer | ||
``` | ||
The username field can be any string field, the roles field has to be a JSON array. | ||
## References | ||
https://micronaut-projects.github.io/micronaut-security/latest/guide/#oauth2-configuration |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package org.akhq.configs; | ||
|
||
import io.micronaut.context.annotation.ConfigurationProperties; | ||
import lombok.Data; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
@ConfigurationProperties("akhq.security.oauth2") | ||
@Data | ||
public class Oauth { | ||
private boolean enabled; | ||
private Map<String, Provider> providers; | ||
|
||
@Data | ||
public static class Provider { | ||
private String label = "Login with OAuth"; | ||
private String usernameField = "login"; | ||
private String groupsField = "organizations_url"; | ||
private String defaultGroup; | ||
private List<GroupMapping> groups = new ArrayList<>(); | ||
private List<UserMapping> users = new ArrayList<>(); | ||
} | ||
|
||
public Provider getProvider(String key) { | ||
providers.putIfAbsent(key, new Provider()); | ||
return providers.get(key); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.akhq.models; | ||
|
||
import io.micronaut.security.token.Claims; | ||
|
||
import java.util.HashMap; | ||
import java.util.Set; | ||
|
||
public class GithubClaims extends HashMap<String, Object> implements Claims { | ||
@Override | ||
public Object get(String name) { | ||
return super.get(name); | ||
} | ||
|
||
@Override | ||
public Set<String> names() { | ||
return super.keySet(); | ||
} | ||
|
||
@Override | ||
public boolean contains(String name) { | ||
return super.containsKey(name); | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
src/main/java/org/akhq/modules/GithubAuthenticationMapper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package org.akhq.modules; | ||
|
||
import io.micronaut.context.annotation.Requires; | ||
import io.micronaut.core.annotation.Nullable; | ||
import io.micronaut.core.util.StringUtils; | ||
import io.micronaut.security.authentication.AuthenticationResponse; | ||
import io.micronaut.security.oauth2.endpoint.authorization.state.State; | ||
import io.micronaut.security.oauth2.endpoint.token.response.OauthAuthenticationMapper; | ||
import io.micronaut.security.oauth2.endpoint.token.response.TokenResponse; | ||
import jakarta.inject.Named; | ||
import org.akhq.configs.Oauth; | ||
import jakarta.inject.Inject; | ||
import jakarta.inject.Singleton; | ||
import org.akhq.models.GithubClaims; | ||
import org.akhq.utils.*; | ||
import org.reactivestreams.Publisher; | ||
import reactor.core.publisher.Flux; | ||
|
||
import java.util.*; | ||
import java.util.stream.Collectors; | ||
|
||
@Singleton | ||
@Named("github") | ||
@Requires(property = "akhq.security.oauth2.enabled", value = StringUtils.TRUE) | ||
public class GithubAuthenticationMapper implements OauthAuthenticationMapper { | ||
@Inject | ||
private Oauth oauth; | ||
@Inject | ||
private GithubApiClient apiClient; | ||
@Inject | ||
private ClaimProvider claimProvider; | ||
|
||
@Override | ||
public Publisher<AuthenticationResponse> createAuthenticationResponse(TokenResponse tokenResponse, @Nullable State state) { | ||
return Flux.from(apiClient.getUser("token " + tokenResponse.getAccessToken())) | ||
.map(user -> { | ||
ClaimRequest request = ClaimRequest.builder() | ||
.providerType(ClaimProviderType.OAUTH) | ||
.providerName("github") | ||
.username(getUsername(oauth.getProvider("github"), user)) | ||
.groups(getOauthGroups(oauth.getProvider("github"), user)) | ||
.build(); | ||
|
||
ClaimResponse claim = claimProvider.generateClaim(request); | ||
|
||
return AuthenticationResponse.success(getUsername(oauth.getProvider("github"), user), claim.getRoles(), claim.getAttributes()); | ||
}); | ||
} | ||
|
||
/** | ||
* Tries to read the username from the configured username field. | ||
* | ||
* @param provider The OAuth provider | ||
* @param user The OAuth claims | ||
* @return The username to set in the {@link io.micronaut.security.authentication.Authentication} | ||
*/ | ||
protected String getUsername(Oauth.Provider provider, GithubClaims user) { | ||
String userNameField = provider.getUsernameField(); | ||
return Objects.toString(user.get(userNameField)); | ||
} | ||
|
||
/** | ||
* Tries to read groups from the configured groups field. | ||
* If the configured field cannot be found or isn't some kind of collection, it will return an empty set. | ||
* | ||
* @param provider The OAuth provider configuration | ||
* @param user The OAuth claims | ||
* @return The groups from oauth | ||
*/ | ||
protected List<String> getOauthGroups(Oauth.Provider provider, GithubClaims user) { | ||
List<String> groups = new ArrayList<>(); | ||
if (user.contains(provider.getGroupsField())) { | ||
Object groupsField = user.get(provider.getGroupsField()); | ||
if (groupsField instanceof Collection) { | ||
groups = ((Collection<Object>) groupsField) | ||
.stream() | ||
.map(Objects::toString) | ||
.collect(Collectors.toList()); | ||
} else if (groupsField instanceof String) { | ||
groups.add((String) groupsField); | ||
} | ||
} | ||
return groups; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,5 +4,6 @@ public enum ClaimProviderType { | |
HEADER, | ||
BASIC_AUTH, | ||
LDAP, | ||
OIDC | ||
OIDC, | ||
OAUTH | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.akhq.utils; | ||
|
||
import io.micronaut.http.annotation.Get; | ||
import io.micronaut.http.annotation.Header; | ||
import io.micronaut.http.client.annotation.Client; | ||
import org.akhq.models.GithubClaims; | ||
import org.reactivestreams.Publisher; | ||
|
||
@Header(name = "User-Agent", value = "Micronaut") | ||
@Header(name = "Accept", value = "application/vnd.github.v3+json, application/json") | ||
@Client("https://api.github.com") | ||
public interface GithubApiClient { | ||
|
||
@Get("/user") | ||
Publisher<GithubClaims> getUser(@Header("Authorization") String authorization); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters