Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial version of the OpenId Connect implementation #313

Merged
merged 7 commits into from
Feb 20, 2022
Merged
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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* Contributors:
* 2021 : Payara Foundation and/or its affiliates
* Initially authored in Security Connectors
*/
package org.glassfish.soteria.openid;


import jakarta.json.JsonObject;
import jakarta.security.enterprise.authentication.mechanism.http.HttpMessageContext;
import jakarta.security.enterprise.credential.Credential;
import jakarta.security.enterprise.identitystore.openid.AccessToken;
import org.glassfish.soteria.openid.domain.AccessTokenImpl;
import org.glassfish.soteria.openid.domain.IdentityTokenImpl;

import static jakarta.security.enterprise.identitystore.openid.OpenIdConstant.*;
import static java.util.Objects.nonNull;

/**
* @author Gaurav Gupta
* @author Rudy De Busscher
*/
public class OpenIdCredential implements Credential {

private final HttpMessageContext httpContext;

private final IdentityTokenImpl identityToken;

private final AccessToken accessToken;

public OpenIdCredential(JsonObject tokensObject, HttpMessageContext httpContext, long tokenMinValidity) {
this.httpContext = httpContext;

this.identityToken = new IdentityTokenImpl(tokensObject.getString(IDENTITY_TOKEN), tokenMinValidity);
String accessTokenString = tokensObject.getString(ACCESS_TOKEN, null);
Long expiresIn = null;
if (nonNull(tokensObject.getJsonNumber(EXPIRES_IN))) {
expiresIn = tokensObject.getJsonNumber(EXPIRES_IN).longValue();
}
String tokenType = tokensObject.getString(TOKEN_TYPE, null);
String scopeString = tokensObject.getString(SCOPE, null);
if (nonNull(accessTokenString)) {
accessToken = new AccessTokenImpl(tokenType, accessTokenString, expiresIn, scopeString, tokenMinValidity);
} else {
accessToken = null;
}
}

/**
* Only for internal use within Soteria to be able to validate the token.
*
* @return Identity Token Implementation
*/
IdentityTokenImpl getIdentityTokenImpl() {
return identityToken;
}

public AccessToken getAccessToken() {
return accessToken;
}

public HttpMessageContext getHttpContext() {
return httpContext;
}

}
160 changes: 160 additions & 0 deletions impl/src/main/java/org/glassfish/soteria/openid/OpenIdExtension.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright (c) 2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* Contributors:
* 2021 : Payara Foundation and/or its affiliates
* Initially authored in Security Connectors
*/
package org.glassfish.soteria.openid;


import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.spi.*;
import jakarta.security.enterprise.authentication.mechanism.http.HttpAuthenticationMechanism;
import jakarta.security.enterprise.identitystore.IdentityStore;
import jakarta.security.enterprise.identitystore.OpenIdAuthenticationDefinition;
import org.glassfish.soteria.openid.controller.*;
import org.glassfish.soteria.openid.domain.OpenIdContextImpl;

import java.util.logging.Logger;

import static java.util.logging.Level.INFO;

/**
* Activates {@link OpenIdAuthenticationMechanism} with the
* {@link OpenIdAuthenticationDefinition} annotation configuration.
*
* @author Gaurav Gupta
* @author Patrik Duditš
* @author Rudy De Busscher
*
*/
public class OpenIdExtension implements Extension {

private static final Logger LOGGER = Logger.getLogger(OpenIdExtension.class.getName());

private OpenIdAuthenticationDefinition definition;

protected void registerTypes(@Observes BeforeBeanDiscovery before) {
registerTypes(before,
AuthenticationController.class,
ConfigurationController.class,
NonceController.class,
ProviderMetadataController.class,
StateController.class,
TokenController.class,
UserInfoController.class,
OpenIdContextImpl.class,
OpenIdIdentityStore.class,
OpenIdAuthenticationMechanism.class,
JWTValidator.class
);
}

private void registerTypes(BeforeBeanDiscovery event, Class<?>... classes) {
for (Class<?> aClass : classes) {
event.addAnnotatedType(aClass, aClass.getName());
}
}

/**
* Find the {@link OpenIdAuthenticationDefinition} annotation and validate.
*/
protected void findOpenIdDefinitionAnnotation(@Observes @WithAnnotations(OpenIdAuthenticationDefinition.class) ProcessAnnotatedType<?> event) {
Class<?> beanClass = event.getAnnotatedType().getJavaClass();
OpenIdAuthenticationDefinition standardDefinition = event.getAnnotatedType().getAnnotation(OpenIdAuthenticationDefinition.class);
setDefinition(standardDefinition, beanClass);
}

private void setDefinition(OpenIdAuthenticationDefinition definition, Class<?> sourceClass) {
if (this.definition != null) {
LOGGER.warning("Multiple authentication definition found. Will ignore the definition in " + sourceClass);
return;
}
validateExtraParametersFormat(definition);
this.definition = definition;
LOGGER.log(INFO, "Activating OpenID Connect authentication definition from class {0}",
sourceClass.getName());
}

protected void validateExtraParametersFormat(OpenIdAuthenticationDefinition definition) {
for (String extraParameter : definition.extraParameters()) {
String[] parts = extraParameter.split("=");
if (parts.length != 2) {
throw new DefinitionException(
OpenIdAuthenticationDefinition.class.getSimpleName()
+ ".extraParameters() value '" + extraParameter
+ "' is not of the format key=value"
);
}
}
}

protected void registerDefinition(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {

if (definition != null) {
LOGGER.log(INFO, "AfterBean Discovery {0}",
definition.getClass().getName());

// if definition is active we broaden the type of OpenIdAuthenticationMechanism back to
// HttpAuthenticationMechanism, so it would be picked up by Jakarta Security.
afterBeanDiscovery.addBean()
.beanClass(HttpAuthenticationMechanism.class)
.addType(HttpAuthenticationMechanism.class)
.id(OpenIdExtension.class.getName() + "/OpenIdAuthenticationMechanism")
.scope(ApplicationScoped.class)
.produceWith(in -> in.select(OpenIdAuthenticationMechanism.class).get())
.disposeWith((inst, callback) -> callback.destroy(inst));

afterBeanDiscovery.addBean()
.beanClass(IdentityStore.class)
.addType(IdentityStore.class)
.id(OpenIdExtension.class.getName() + "/OpenIdIdentityStore")
.scope(ApplicationScoped.class)
.produceWith(in -> in.select(OpenIdIdentityStore.class).get())
.disposeWith((inst, callback) -> callback.destroy(inst));

/*
afterBeanDiscovery.addBean()
.beanClass(OpenIdContextImpl.class)
.addType(OpenIdContext.class)
.id(OpenIdExtension.class.getName() + "/OpenIdContext")
.scope(SessionScoped.class)
.produceWith(in -> in.select(OpenIdContextImpl.class).get())
.disposeWith((inst, callback) -> callback.destroy(inst));
*/

afterBeanDiscovery.addBean()
.beanClass(OpenIdAuthenticationDefinition.class)
.types(OpenIdAuthenticationDefinition.class)
.scope(ApplicationScoped.class)
.id("OpenId Definition")
.createWith(cc -> this.definition);


} else {
// Publish empty definition to prevent injection errors. The helper components will not work, but
// will not cause definition error. This is quite unlucky situation, but when definition is on an
// alternative bean we don't know before this moment whether the bean is enabled or not.
afterBeanDiscovery.addBean()
.beanClass(OpenIdAuthenticationDefinition.class)
.types(OpenIdAuthenticationDefinition.class)
.scope(Dependent.class)
.id("Null OpenId Definition")
.createWith(cc -> null);
}
}

}
Loading