[GitHub] [cxf] coheigea commented on a change in pull request #580: CXF-8121 Improve STS REST interface

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[GitHub] [cxf] coheigea commented on a change in pull request #580: CXF-8121 Improve STS REST interface

GitBox

coheigea commented on a change in pull request #580:
URL: https://github.com/apache/cxf/pull/580#discussion_r444248658



##########
File path: services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/rest/STSRealmRestTest.java
##########
@@ -0,0 +1,656 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.systest.sts.rest;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.xml.bind.JAXBElement;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.common.util.CompressionUtils;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.rt.security.claims.Claim;
+import org.apache.cxf.rt.security.claims.ClaimCollection;
+import org.apache.cxf.rt.security.crypto.CryptoUtils;
+import org.apache.cxf.rt.security.saml.utils.SAMLUtils;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.rest.api.GetTokenRequest;
+import org.apache.cxf.sts.rest.api.RealmSecurityTokenService;
+import org.apache.cxf.sts.rest.api.TokenRequest;
+import org.apache.cxf.systest.sts.common.SecurityTestUtil;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoFactory;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.saml.SAMLKeyInfo;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.dom.WSDocInfo;
+import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
+import org.apache.wss4j.dom.handler.RequestData;
+import org.apache.wss4j.dom.processor.SAMLTokenProcessor;
+import org.apache.xml.security.utils.ClassLoaderUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Some tests for the REST interface of the CXF STS.
+ */
+public class STSRealmRestTest extends AbstractBusClientServerTestBase {
+
+    private static final String STSPORT = allocatePort(STSRealmRestServer.class);
+
+    private static final String REALM = "realmA";
+
+    private static final String SAML1_TOKEN_TYPE =
+        "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1";
+    private static final String SAML2_TOKEN_TYPE =
+        "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
+    private static final String JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt";
+    private static final String DEFAULT_ADDRESS =
+        "https://localhost:8081/doubleit/services/doubleittransportsaml1";
+
+    private static TLSClientParameters tlsClientParameters = new TLSClientParameters();
+    private static Crypto serviceCrypto;
+
+    @org.junit.BeforeClass
+    public static void startServers() throws Exception {
+        assertTrue(
+                   "Server failed to launch",
+                   // run the server in the same process
+                   // set this to false to fork
+                   launchServer(STSRealmRestServer.class, true)
+        );
+
+        tlsClientParameters = getTLSClientParameters();
+        serviceCrypto = CryptoFactory.getInstance("serviceKeystore.properties");
+    }
+
+    @org.junit.AfterClass
+    public static void cleanup() throws Exception {
+        SecurityTestUtil.cleanup();
+        stopAllServers();
+    }
+
+    @org.junit.Test
+    public void testIssueSAML2Token() throws Exception {
+        Document assertionDoc = client().getXMLToken(REALM, "saml2.0", null, null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+    }
+
+    @org.junit.Test
+    public void testIssueSAML1Token() throws Exception {
+        Document assertionDoc = client().getXMLToken(REALM, "saml1.1", null, null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null);
+    }
+
+    @org.junit.Test
+    public void testIssueSymmetricKeySaml1() throws Exception {
+        Document assertionDoc = client()
+            .getXMLToken(REALM, "saml1.1", STSConstants.SYMMETRIC_KEY_KEYTYPE, null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null);
+
+        List<String> methods = assertion.getConfirmationMethods();
+        String confirmMethod = null;
+        if (methods != null && !methods.isEmpty()) {
+            confirmMethod = methods.get(0);
+        }
+        assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod));
+        SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo();
+        assertNotNull(subjectKeyInfo.getSecret());
+    }
+
+    @org.junit.Test
+    public void testIssueSymmetricKeySaml1ShortKeyType() throws Exception {
+        Document assertionDoc = client()
+            .getXMLToken(REALM, "saml1.1", "SymmetricKey", null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null);
+
+        List<String> methods = assertion.getConfirmationMethods();
+        String confirmMethod = null;
+        if (methods != null && !methods.isEmpty()) {
+            confirmMethod = methods.get(0);
+        }
+        assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod));
+        SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo();
+        assertNotNull(subjectKeyInfo.getSecret());
+    }
+
+    @org.junit.Test
+    public void testIssuePublicKeySAML2Token() throws Exception {
+        Document assertionDoc = client()
+            .getXMLToken(REALM, "saml2.0", STSConstants.PUBLIC_KEY_KEYTYPE, null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+
+        List<String> methods = assertion.getConfirmationMethods();
+        String confirmMethod = null;
+        if (methods != null && !methods.isEmpty()) {
+            confirmMethod = methods.get(0);
+        }
+        assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod));
+        SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo();
+        assertNotNull(subjectKeyInfo.getCerts());
+    }
+
+    @org.junit.Test
+    public void testIssuePublicKeySAML2TokenShortKeyType() throws Exception {
+        Document assertionDoc = client()
+            .getXMLToken(REALM, "saml2.0", "PublicKey", null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+
+        List<String> methods = assertion.getConfirmationMethods();
+        String confirmMethod = null;
+        if (methods != null && !methods.isEmpty()) {
+            confirmMethod = methods.get(0);
+        }
+        assertTrue(OpenSAMLUtil.isMethodHolderOfKey(confirmMethod));
+        SAMLKeyInfo subjectKeyInfo = assertion.getSubjectKeyInfo();
+        assertNotNull(subjectKeyInfo.getCerts());
+    }
+
+    @org.junit.Test
+    public void testIssueBearerSAML1Token() throws Exception {
+        Document assertionDoc = client()
+            .getXMLToken(REALM, "saml1.1", STSConstants.BEARER_KEY_KEYTYPE, null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null);
+
+        List<String> methods = assertion.getConfirmationMethods();
+        String confirmMethod = null;
+        if (methods != null && !methods.isEmpty()) {
+            confirmMethod = methods.get(0);
+        }
+        assertTrue(confirmMethod.contains("bearer"));
+    }
+
+    @org.junit.Test
+    public void testIssueBearerSAML1TokenShorKeyType() throws Exception {
+        Document assertionDoc = client()
+            .getXMLToken(REALM, "saml1.1", "Bearer", null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null);
+
+        List<String> methods = assertion.getConfirmationMethods();
+        String confirmMethod = null;
+        if (methods != null && !methods.isEmpty()) {
+            confirmMethod = methods.get(0);
+        }
+        assertTrue(confirmMethod.contains("bearer"));
+    }
+
+    @org.junit.Test
+    public void testIssueSAML2TokenAppliesTo() throws Exception {
+        Document assertionDoc = client()
+            .getXMLToken(REALM, "saml2.0", null, null, DEFAULT_ADDRESS, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+    }
+
+    @org.junit.Test
+    public void testIssueSAML2TokenUnknownAppliesTo() throws Exception {
+        Response response = client()
+            .getXMLToken(REALM, "saml2.0", null, null, "https://localhost:8081/tripleit/", false);
+        try {
+            response.readEntity(Document.class);
+            fail("Failure expected on an unknown AppliesTo address");
+        } catch (Exception ex) {
+            // expected
+        }
+    }
+
+    @org.junit.Test
+    public void testIssueSAML2TokenClaims() throws Exception {
+        // First check that the role isn't usually in the generated token
+        Document assertionDoc = client().getXMLToken(REALM, "saml2.0", null, null, null, false)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+
+        ClaimCollection claims = SAMLUtils.getClaims(assertion);
+        assertEquals(1, claims.size());
+        Claim claim = claims.get(0);
+        String role = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role";
+        assertNotEquals(role, claim.getClaimType().toString());
+
+        // Now get another token specifying the role
+        assertionDoc = client().getXMLToken(REALM, "saml2.0", null, Collections.singletonList(role), null, false)
+            .readEntity(Document.class);
+
+        assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+
+        claims = SAMLUtils.getClaims(assertion);
+        assertEquals(1, claims.size());
+        claim = claims.get(0);
+        assertEquals(role, claim.getClaimType().toString());
+        assertEquals("ordinary-user", claim.getValues().get(0));
+    }
+
+    @org.junit.Test
+    public void testIssueSAML2TokenViaWSTrust() throws Exception {
+        RequestSecurityTokenResponseType securityResponse = client()
+            .getXMLToken(REALM, "saml2.0", null, null, null, true).readEntity(RequestSecurityTokenResponseType.class);
+
+        validateSAMLSecurityTokenResponse(securityResponse, true);
+    }
+
+    @org.junit.Test
+    public void testIssueSAML2TokenViaPOST() throws Exception {
+        // Create GetTokenRequest
+        GetTokenRequest getTokenRequest = new GetTokenRequest();
+        getTokenRequest.setTokenType(SAML2_TOKEN_TYPE);
+
+        Document assertionDoc = client().getToken(REALM, getTokenRequest)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+    }
+
+    @org.junit.Test
+    public void testIssueSAML1TokenViaPOST() throws Exception {
+        // Create GetTokenRequest
+        GetTokenRequest getTokenRequest = new GetTokenRequest();
+        getTokenRequest.setTokenType(SAML1_TOKEN_TYPE);
+
+        Document assertionDoc = client().getToken(REALM, getTokenRequest)
+            .readEntity(Document.class);
+
+        SamlAssertionWrapper assertion = validateSAMLToken(assertionDoc);
+        assertTrue(assertion.getSaml2() == null && assertion.getSaml1() != null);
+    }
+
+    @org.junit.Test
+    public void testValidateSAML2Token() throws Exception {
+        // 1. Get a token via GET
+        Document assertionDoc = client().getXMLToken(REALM, "saml2.0", null, null, null, false)
+            .readEntity(Document.class);
+        assertNotNull(assertionDoc);
+
+        // 2. Now validate it in the STS using POST
+        TokenRequest tokenRequest = new TokenRequest();
+        tokenRequest.setToken(assertionDoc.getDocumentElement());
+        // TODO: remove
+        tokenRequest.setTokenType(STSConstants.STATUS);
+        RequestSecurityTokenResponseType securityResponse = client().validate(REALM, tokenRequest)
+            .readEntity(RequestSecurityTokenResponseType.class);
+
+        assertTrue(getValidationStatus(securityResponse));
+    }
+
+    @org.junit.Test
+    public void testRenewSAML2Token() throws Exception {
+        // 1. Get a token via GET
+        Document assertionDoc = client().getXMLToken(REALM, "saml2.0", null, null, null, false)
+            .readEntity(Document.class);
+        assertNotNull(assertionDoc);
+
+        // 2. Now renew it using POST
+        TokenRequest tokenRequest = new TokenRequest();
+        tokenRequest.setToken(assertionDoc.getDocumentElement());
+        RequestSecurityTokenResponseType securityResponse = client().renew(REALM, tokenRequest)
+            .readEntity(RequestSecurityTokenResponseType.class);
+
+        validateSAMLSecurityTokenResponse(securityResponse, true);
+    }
+
+    @org.junit.Test
+    public void testIssueSAML2TokenPlain() throws Exception {
+        String encodedAssertion = client().getPlainToken(REALM, "saml2.0", null, null, null)
+            .readEntity(String.class);
+        assertNotNull(encodedAssertion);
+
+        byte[] deflatedToken = Base64Utility.decode(encodedAssertion);
+        InputStream inputStream = CompressionUtils.inflate(deflatedToken);
+        Document doc =
+            StaxUtils.read(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+
+        SamlAssertionWrapper assertion = validateSAMLToken(doc);
+        assertTrue(assertion.getSaml2() != null && assertion.getSaml1() == null);
+    }
+//
+    @org.junit.Test
+    public void testIssueJWTTokenPlain() throws Exception {
+        String token = client().getPlainToken(REALM, "jwt", null, null, null)
+            .readEntity(String.class);
+        validateJWTToken(token);
+    }
+
+    @org.junit.Test
+    public void testIssueJWTTokenAppliesTo() throws Exception {
+        String token = client().getPlainToken(REALM, "jwt", null, null, DEFAULT_ADDRESS)
+            .readEntity(String.class);
+        JwtToken jwt = validateJWTToken(token);
+
+        List<String> audiences = jwt.getClaims().getAudiences();
+        assertEquals(1, audiences.size());
+        assertEquals(DEFAULT_ADDRESS, audiences.get(0));
+    }
+
+    @org.junit.Test
+    public void testIssueJWTTokenClaims() throws Exception {
+        // First check that the role isn't usually in the generated token
+
+        String token = client().getPlainToken(REALM, "jwt", null, null, null)
+            .readEntity(String.class);
+        JwtToken jwt = validateJWTToken(token);
+
+        assertNull(jwt.getClaim("roles"));
+
+        // Now get another token specifying the role
+        token = client().getPlainToken(REALM, "jwt", null, Collections.singletonList("roles"), null)
+            .readEntity(String.class);
+        jwt = validateJWTToken(token);
+
+        assertEquals("ordinary-user", jwt.getClaim("roles"));
+    }
+
+    @org.junit.Test
+    public void testIssueJWTTokenViaPOST() throws Exception {
+        GetTokenRequest getTokenRequest = new GetTokenRequest();
+        getTokenRequest.setTokenType(JWT_TOKEN_TYPE);
+
+        Document assertionDoc = client().getToken(REALM, getTokenRequest)
+            .readEntity(Document.class);
+
+        // Process the token
+        validateJWTToken(assertionDoc.getDocumentElement().getFirstChild().getTextContent());
+    }
+
+    @org.junit.Test
+    public void testValidateSAMLAndIssueJWT() throws Exception {
+        // 1. Get a token via GET
+        Document assertionDoc = client().getXMLToken(REALM, "saml2.0", null, null, null, false)
+            .readEntity(Document.class);
+        assertNotNull(assertionDoc);
+
+        // 2. Now validate it in the STS using POST
+        TokenRequest tokenRequest = new TokenRequest();
+        tokenRequest.setToken(assertionDoc.getDocumentElement());
+        tokenRequest.setTokenType(JWT_TOKEN_TYPE);
+        RequestSecurityTokenResponseType securityResponse = client().validate(REALM, tokenRequest)
+            .readEntity(RequestSecurityTokenResponseType.class);
+
+        assertTrue(getValidationStatus(securityResponse));
+
+        // Check the token
+        RequestedSecurityTokenType requestedSecurityToken = getRequestedSecurityToken(securityResponse);
+
+        String token = ((Element)requestedSecurityToken.getAny()).getTextContent();
+        assertNotNull(token);
+
+        validateJWTToken(token);
+    }
+
+    @org.junit.Test
+    public void testValidateJWTAndIssueSAML() throws Exception {
+        // 1. Get a token via GET
+        Document token = client().getXMLToken(REALM, "jwt", null, null, null, false)
+            .readEntity(Document.class);
+        assertNotNull(token);
+
+        // 2. Now validate it in the STS using POST
+        TokenRequest tokenRequest = new TokenRequest();
+        tokenRequest.setToken(token.getDocumentElement());
+        tokenRequest.setTokenType(SAML2_TOKEN_TYPE);
+        RequestSecurityTokenResponseType securityResponse = client().validate(REALM, tokenRequest)
+            .readEntity(RequestSecurityTokenResponseType.class);
+
+        assertTrue(getValidationStatus(securityResponse));
+
+        // Check the token
+        validateSAMLSecurityTokenResponse(securityResponse, true);
+    }
+
+    @org.junit.Test
+    public void testIssueJWTTokenXMLWrapper() throws Exception {
+        Document assertionDoc = client().getXMLToken(REALM, "jwt", null, null, null, false)
+            .readEntity(Document.class);
+        assertNotNull(assertionDoc);
+
+        // Discard XML wrapper
+        validateJWTToken(assertionDoc.getDocumentElement().getFirstChild().getTextContent());
+    }
+
+    @org.junit.Test
+    public void testIssueJWTTokenJSONWrapper() throws Exception {
+        InputStream response = client().getJSONToken(REALM, "jwt", null, null, null)
+            .readEntity(InputStream.class);
+        assertNotNull(response);
+
+        String token = new ObjectMapper().readTree(response).get("token").asText();
+        validateJWTToken(token);
+    }
+
+    @org.junit.Ignore

Review comment:
       Why are these two tests Ignored?

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/authentication/JwtAuthenticationFilter.java
##########
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.authentication;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.security.SimplePrincipal;
+import org.apache.cxf.jaxws.context.WrappedMessageContext;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.sts.QNameConstants;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.operation.TokenValidateOperation;
+import org.apache.cxf.sts.rest.impl.TokenUtils;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
+import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.cxf.common.util.StringUtils.isEmpty;
+import static org.apache.cxf.jaxrs.utils.JAXRSUtils.getCurrentMessage;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.createValidateRequestSecurityTokenType;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.getEncodedJwtToken;
+
+@PreMatching
+@Priority(AUTHENTICATION)
+public class JwtAuthenticationFilter implements ContainerRequestFilter {
+    private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+    private static final QName QNAME_WST_STATUS =
+            QNameConstants.WS_TRUST_FACTORY.createStatus(null).getName();
+    private static final String AUTHENTICATION_SCHEME = "Bearer";
+    private TokenValidateOperation validateOperation;
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        if (context.getSecurityContext().getUserPrincipal() != null) {
+            LOG.debug("User principal is already set, pass filter without authentication processing");
+            return;
+        }
+
+        final String authorizationHeader  = context.getHeaderString(AUTHORIZATION);
+        if (isEmpty(authorizationHeader)) {
+            return; //abortUnauthorized(context, "This is no Authorization header in the request");
+        }
+
+        if (!isTokenBasedAuthentication(authorizationHeader)) {
+            abortUnauthorized(context, "Authorization header is present, but is not Bearer schema");
+        }
+
+        if (ofNullable(validateOperation).isPresent()) {
+            try {
+                validateToken(context, authorizationHeader);
+            } catch (Exception e) {
+                abortUnauthorized(context, e.getMessage());
+            }
+        } else {
+            LOG.debug("ValidationOperation bean is not defined");
+            abortUnauthorized(context, null);
+        }
+    }
+
+    private boolean isTokenBasedAuthentication(String authorizationHeader) {
+        return !isEmpty(authorizationHeader)
+            && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
+    }
+
+    private void abortUnauthorized(final ContainerRequestContext context, final String message) {
+        LOG.error("Authorization error: {}", message);
+        context.abortWith(status(UNAUTHORIZED).build());
+    }
+
+    private void validateToken(final ContainerRequestContext context, final String authorizationHeader)
+        throws Exception {
+        final String tokenString = getEncodedJwtToken(authorizationHeader);
+        if (isEmpty(tokenString)) {
+            throw new NotAuthorizedException("Bearer schema is present but token is empty",
+                TokenUtils.BEARER_AUTH_SCHEMA);
+        }
+
+        final Message message = getCurrentMessage();
+        final RequestSecurityTokenResponseType response = validateOperation.validate(
+            createValidateRequestSecurityTokenType(tokenString, "jwt"), null,
+            new WrappedMessageContext(message));
+        if (validateResponse(response)) {
+            createSecurityContext(context, tokenString);
+        } else {
+            throw new NotAuthorizedException("Bearer token validation is failed", TokenUtils.BEARER_AUTH_SCHEMA);
+        }
+    }
+
+    private void createSecurityContext(final ContainerRequestContext context, final String token) {
+        final JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token);
+        final JwtToken jwt = jwtConsumer.getJwtToken();
+        final SecurityContext securityContext = context.getSecurityContext();
+        final SecurityContext newSecurityContext = new SecurityContext() {
+
+            public Principal getUserPrincipal() {
+                return ofNullable(jwt)
+                        .map(j -> j.getClaims())
+                        .map(c -> c.getSubject())
+                        .map(SimplePrincipal::new)
+                        .orElse(null);
+            }
+
+            public boolean isUserInRole(String role) {
+                List<String> roles = (List<String>) ofNullable(jwt)
+                        .map(j -> j.getClaims())
+                        .map(c -> c.getClaim("roles"))
+                        .orElse(null);

Review comment:
       Maybe this parsing could be done in a constructor to avoid having to do it with every call to "isUserInRole"?

##########
File path: services/sts/sts-core/src/main/java/org/apache/cxf/sts/rest/RESTSecurityTokenServiceImpl.java
##########
@@ -69,7 +69,7 @@
     public static final Map<String, String> DEFAULT_CLAIM_TYPE_MAP;
     public static final Map<String, String> DEFAULT_TOKEN_TYPE_MAP;
 
-    private static final Map<String, String> DEFAULT_KEY_TYPE_MAP = new HashMap<>();
+    public static final Map<String, String> DEFAULT_KEY_TYPE_MAP = new HashMap<>();

Review comment:
       This map should be made an unmodifiable map as per the other public maps

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/api/BaseResponse.java
##########
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.api;
+
+import java.io.Serializable;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+/**
+ * Base class for STS REST interface responses
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "BaseResponse", propOrder = {
+        "status",
+        "message"
+})
+@XmlRootElement(name = "BaseResponse")
+public class BaseResponse implements Serializable {

Review comment:
       This class seems only to be called in the RestExceptionMapper when there is an exception? Maybe it should be renamed for only the purpose of an exception?

##########
File path: services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/rest/STSRealmRestTest.java
##########
@@ -0,0 +1,656 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.systest.sts.rest;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.xml.bind.JAXBElement;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.common.util.CompressionUtils;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.rt.security.claims.Claim;
+import org.apache.cxf.rt.security.claims.ClaimCollection;
+import org.apache.cxf.rt.security.crypto.CryptoUtils;
+import org.apache.cxf.rt.security.saml.utils.SAMLUtils;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.rest.api.GetTokenRequest;
+import org.apache.cxf.sts.rest.api.RealmSecurityTokenService;
+import org.apache.cxf.sts.rest.api.TokenRequest;
+import org.apache.cxf.systest.sts.common.SecurityTestUtil;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoFactory;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.saml.SAMLKeyInfo;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.dom.WSDocInfo;
+import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
+import org.apache.wss4j.dom.handler.RequestData;
+import org.apache.wss4j.dom.processor.SAMLTokenProcessor;
+import org.apache.xml.security.utils.ClassLoaderUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Some tests for the REST interface of the CXF STS.
+ */
+public class STSRealmRestTest extends AbstractBusClientServerTestBase {
+
+    private static final String STSPORT = allocatePort(STSRealmRestServer.class);
+
+    private static final String REALM = "realmA";
+
+    private static final String SAML1_TOKEN_TYPE =
+        "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1";
+    private static final String SAML2_TOKEN_TYPE =
+        "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
+    private static final String JWT_TOKEN_TYPE = "urn:ietf:params:oauth:token-type:jwt";
+    private static final String DEFAULT_ADDRESS =
+        "https://localhost:8081/doubleit/services/doubleittransportsaml1";
+
+    private static TLSClientParameters tlsClientParameters = new TLSClientParameters();
+    private static Crypto serviceCrypto;
+
+    @org.junit.BeforeClass
+    public static void startServers() throws Exception {
+        assertTrue(
+                   "Server failed to launch",
+                   // run the server in the same process
+                   // set this to false to fork
+                   launchServer(STSRealmRestServer.class, true)
+        );
+
+        tlsClientParameters = getTLSClientParameters();
+        serviceCrypto = CryptoFactory.getInstance("serviceKeystore.properties");
+    }
+
+    @org.junit.AfterClass
+    public static void cleanup() throws Exception {
+        SecurityTestUtil.cleanup();

Review comment:
       This method is removed in the latest codebase

##########
File path: services/sts/sts-rest/pom.xml
##########
@@ -0,0 +1,72 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.cxf.services.sts</groupId>
+    <artifactId>cxf-services-sts-rest</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache CXF STS REST</name>
+    <url>https://cxf.apache.org</url>
+    <parent>
+        <groupId>org.apache.cxf</groupId>
+        <artifactId>cxf-parent</artifactId>
+        <version>3.4.0-SNAPSHOT</version>
+        <relativePath>../../../parent/pom.xml</relativePath>
+    </parent>
+    <properties>
+        <cxf.module.name>org.apache.cxf.sts.rest</cxf.module.name>
+        <cxf.osgi.import>
+            javax.servlet*;version="${cxf.osgi.javax.servlet.version}",
+            !org.apache.cxf.sts.*,
+            org.apache.cxf.*,
+            org.springframework.ldap*;resolution:=optional,
+            net.sf.ehcache*;resolution:=optional;version="[2.5, 3.0.0)",
+            org.opensaml*;version="${cxf.opensaml.osgi.version.range}",

Review comment:
       I think these three lines can be removed.

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/authentication/JwtAuthenticationFilter.java
##########
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.authentication;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.security.SimplePrincipal;
+import org.apache.cxf.jaxws.context.WrappedMessageContext;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.sts.QNameConstants;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.operation.TokenValidateOperation;
+import org.apache.cxf.sts.rest.impl.TokenUtils;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
+import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.cxf.common.util.StringUtils.isEmpty;
+import static org.apache.cxf.jaxrs.utils.JAXRSUtils.getCurrentMessage;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.createValidateRequestSecurityTokenType;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.getEncodedJwtToken;
+
+@PreMatching
+@Priority(AUTHENTICATION)
+public class JwtAuthenticationFilter implements ContainerRequestFilter {

Review comment:
       Couldn't this class be abstracted so that it works for other tokens as well that are capable of being validated by the local STS instance?

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/authentication/JwtAuthenticationFilter.java
##########
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.authentication;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.security.SimplePrincipal;
+import org.apache.cxf.jaxws.context.WrappedMessageContext;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.sts.QNameConstants;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.operation.TokenValidateOperation;
+import org.apache.cxf.sts.rest.impl.TokenUtils;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
+import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.cxf.common.util.StringUtils.isEmpty;
+import static org.apache.cxf.jaxrs.utils.JAXRSUtils.getCurrentMessage;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.createValidateRequestSecurityTokenType;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.getEncodedJwtToken;
+
+@PreMatching
+@Priority(AUTHENTICATION)
+public class JwtAuthenticationFilter implements ContainerRequestFilter {
+    private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+    private static final QName QNAME_WST_STATUS =
+            QNameConstants.WS_TRUST_FACTORY.createStatus(null).getName();
+    private static final String AUTHENTICATION_SCHEME = "Bearer";
+    private TokenValidateOperation validateOperation;
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        if (context.getSecurityContext().getUserPrincipal() != null) {
+            LOG.debug("User principal is already set, pass filter without authentication processing");
+            return;
+        }
+
+        final String authorizationHeader  = context.getHeaderString(AUTHORIZATION);
+        if (isEmpty(authorizationHeader)) {
+            return; //abortUnauthorized(context, "This is no Authorization header in the request");
+        }
+
+        if (!isTokenBasedAuthentication(authorizationHeader)) {
+            abortUnauthorized(context, "Authorization header is present, but is not Bearer schema");
+        }
+
+        if (ofNullable(validateOperation).isPresent()) {
+            try {
+                validateToken(context, authorizationHeader);
+            } catch (Exception e) {
+                abortUnauthorized(context, e.getMessage());
+            }
+        } else {
+            LOG.debug("ValidationOperation bean is not defined");
+            abortUnauthorized(context, null);
+        }
+    }
+
+    private boolean isTokenBasedAuthentication(String authorizationHeader) {
+        return !isEmpty(authorizationHeader)
+            && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
+    }
+
+    private void abortUnauthorized(final ContainerRequestContext context, final String message) {
+        LOG.error("Authorization error: {}", message);
+        context.abortWith(status(UNAUTHORIZED).build());
+    }
+
+    private void validateToken(final ContainerRequestContext context, final String authorizationHeader)
+        throws Exception {
+        final String tokenString = getEncodedJwtToken(authorizationHeader);
+        if (isEmpty(tokenString)) {
+            throw new NotAuthorizedException("Bearer schema is present but token is empty",
+                TokenUtils.BEARER_AUTH_SCHEMA);
+        }
+
+        final Message message = getCurrentMessage();
+        final RequestSecurityTokenResponseType response = validateOperation.validate(
+            createValidateRequestSecurityTokenType(tokenString, "jwt"), null,
+            new WrappedMessageContext(message));

Review comment:
       Why WrappedMessageContext here? We can just pass through "message" and then eliminate the jaxws frontend from the pom.

##########
File path: services/sts/systests/basic/src/test/java/org/apache/cxf/systest/sts/rest/STSRealmRestTest.java
##########
@@ -0,0 +1,656 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.systest.sts.rest;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyStore;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.TrustManagerFactory;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.xml.bind.JAXBElement;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.cxf.common.util.Base64Utility;
+import org.apache.cxf.common.util.CompressionUtils;
+import org.apache.cxf.configuration.jsse.TLSClientParameters;
+import org.apache.cxf.jaxrs.client.JAXRSClientFactory;
+import org.apache.cxf.jaxrs.client.WebClient;
+import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.rt.security.claims.Claim;
+import org.apache.cxf.rt.security.claims.ClaimCollection;
+import org.apache.cxf.rt.security.crypto.CryptoUtils;
+import org.apache.cxf.rt.security.saml.utils.SAMLUtils;
+import org.apache.cxf.staxutils.StaxUtils;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.rest.api.GetTokenRequest;
+import org.apache.cxf.sts.rest.api.RealmSecurityTokenService;
+import org.apache.cxf.sts.rest.api.TokenRequest;
+import org.apache.cxf.systest.sts.common.SecurityTestUtil;
+import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.RequestedSecurityTokenType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.apache.wss4j.common.crypto.Crypto;
+import org.apache.wss4j.common.crypto.CryptoFactory;
+import org.apache.wss4j.common.crypto.CryptoType;
+import org.apache.wss4j.common.saml.OpenSAMLUtil;
+import org.apache.wss4j.common.saml.SAMLKeyInfo;
+import org.apache.wss4j.common.saml.SamlAssertionWrapper;
+import org.apache.wss4j.dom.WSDocInfo;
+import org.apache.wss4j.dom.engine.WSSecurityEngineResult;
+import org.apache.wss4j.dom.handler.RequestData;
+import org.apache.wss4j.dom.processor.SAMLTokenProcessor;
+import org.apache.xml.security.utils.ClassLoaderUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * Some tests for the REST interface of the CXF STS.
+ */
+public class STSRealmRestTest extends AbstractBusClientServerTestBase {

Review comment:
       There is a huge amount of duplicated code between this class and STSRESTTest. I wonder if we can't abstract the tests to a common base class, and subclass just the bits that get the tokens?

##########
File path: services/sts/systests/basic/src/test/resources/org/apache/cxf/systest/sts/rest/cxf-realm-rest-sts.xml
##########
@@ -0,0 +1,164 @@
+<?xml version="1.0"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements. See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership. The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License. You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied. See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+    xmlns:cxf="http://cxf.apache.org/core"
+    xmlns:sec="http://cxf.apache.org/configuration/security"
+    xmlns:httpj="http://cxf.apache.org/transports/http-jetty/configuration"
+    xmlns:util="http://www.springframework.org/schema/util"
+    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="
+        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
+        http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd
+        http://cxf.apache.org/transports/http-jetty/configuration http://cxf.apache.org/schemas/configuration/http-jetty.xsd
+        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
+        http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
+
+    <import resource="classpath:META-INF/cxf/cxf.xml"/>
+
+    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"/>

Review comment:
       Change all instances of PropertyPlaceholderConfigurer to org.springframework.context.support.PropertySourcesPlaceholderConfigurer instead

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/authentication/JwtAuthenticationFilter.java
##########
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.authentication;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.security.SimplePrincipal;
+import org.apache.cxf.jaxws.context.WrappedMessageContext;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.sts.QNameConstants;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.operation.TokenValidateOperation;
+import org.apache.cxf.sts.rest.impl.TokenUtils;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
+import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.cxf.common.util.StringUtils.isEmpty;
+import static org.apache.cxf.jaxrs.utils.JAXRSUtils.getCurrentMessage;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.createValidateRequestSecurityTokenType;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.getEncodedJwtToken;
+
+@PreMatching
+@Priority(AUTHENTICATION)
+public class JwtAuthenticationFilter implements ContainerRequestFilter {
+    private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+    private static final QName QNAME_WST_STATUS =
+            QNameConstants.WS_TRUST_FACTORY.createStatus(null).getName();
+    private static final String AUTHENTICATION_SCHEME = "Bearer";
+    private TokenValidateOperation validateOperation;
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        if (context.getSecurityContext().getUserPrincipal() != null) {
+            LOG.debug("User principal is already set, pass filter without authentication processing");
+            return;
+        }
+
+        final String authorizationHeader  = context.getHeaderString(AUTHORIZATION);
+        if (isEmpty(authorizationHeader)) {
+            return; //abortUnauthorized(context, "This is no Authorization header in the request");
+        }
+
+        if (!isTokenBasedAuthentication(authorizationHeader)) {
+            abortUnauthorized(context, "Authorization header is present, but is not Bearer schema");
+        }
+
+        if (ofNullable(validateOperation).isPresent()) {
+            try {
+                validateToken(context, authorizationHeader);
+            } catch (Exception e) {
+                abortUnauthorized(context, e.getMessage());
+            }
+        } else {
+            LOG.debug("ValidationOperation bean is not defined");
+            abortUnauthorized(context, null);
+        }
+    }
+
+    private boolean isTokenBasedAuthentication(String authorizationHeader) {
+        return !isEmpty(authorizationHeader)

Review comment:
       We already know that it's not empty as per line 76 above.

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/impl/RealmSecurityConfigurationFilter.java
##########
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.impl;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+
+import org.apache.cxf.helpers.CastUtils;
+import org.apache.cxf.jaxrs.impl.MetadataMap;
+import org.apache.cxf.jaxrs.model.ClassResourceInfo;
+import org.apache.cxf.jaxrs.utils.JAXRSUtils;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.sts.rest.token.realm.ExtRealmProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.HttpMethod.POST;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.HttpHeaders.ACCEPT;
+import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE;
+import static javax.ws.rs.core.MediaType.WILDCARD;
+import static org.apache.cxf.jaxrs.utils.HttpUtils.getPathToMatch;
+import static org.apache.cxf.jaxrs.utils.HttpUtils.getProtocolHeader;
+import static org.apache.cxf.message.Message.HTTP_REQUEST_METHOD;
+import static org.apache.cxf.message.Message.PROTOCOL_HEADERS;
+import static org.springframework.util.CollectionUtils.isEmpty;

Review comment:
       Replace this with: org.apache.cxf.common.util.StringUtils.isEmpty

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/authentication/STSJaasAuthenticationFilter.java
##########
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.authentication;
+
+import java.util.Map;
+import java.util.Optional;
+
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+
+import org.apache.cxf.jaxrs.security.JAASAuthenticationFilter;
+import org.apache.cxf.sts.rest.token.realm.ExtRealmProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.SecurityContext.BASIC_AUTH;
+import static org.apache.cxf.jaxrs.utils.JAXRSUtils.getCurrentMessage;
+import static org.apache.cxf.sts.rest.impl.RealmSecurityConfigurationFilter.REALM_NAME_PARAM;
+
+@PreMatching
+@Priority(AUTHENTICATION)
+public class STSJaasAuthenticationFilter extends JAASAuthenticationFilter {
+    private static final Logger LOG = LoggerFactory.getLogger(STSJaasAuthenticationFilter.class);
+    private static final String JAAS_CONTEXT_NAME_PARAM = "rs.security.auth.jaas.context.name";
+    private Map<String, Object> realmMap;
+
+    @Override
+    public void filter(final ContainerRequestContext context) {
+        final SecurityContext securityContext = context.getSecurityContext();
+        if (securityContext.getUserPrincipal() != null) {
+            LOG.debug("User principal is already set, pass filter without authentication processing");
+            return;
+        }
+
+        if (!BASIC_AUTH.equals(securityContext.getAuthenticationScheme())) {
+            LOG.debug("Authorization schema is not BASIC, pass filter without authentication processing");
+            return;
+        }
+
+        final String realmName = (String)getCurrentMessage().get(REALM_NAME_PARAM.toUpperCase());
+        final Optional<String> contextName = ofNullable(realmName)
+                .map(n -> realmMap.get(n.toUpperCase()))
+                .map(o -> (ExtRealmProperties) o)
+                .map(extRealmProperties -> (String)extRealmProperties.getRsSecurityProperty(JAAS_CONTEXT_NAME_PARAM));
+
+        if (contextName.isPresent()) {
+            setRealmName(realmName);
+            setContextName(contextName.get());
+            super.filter(context);
+        } else {
+            LOG.debug("There is not JAAS context configured, pass filter without authentication processing");

Review comment:
       "There is not JAAS context configured" -> "A JAAS context is not configured"

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/authentication/JwtAuthenticationFilter.java
##########
@@ -0,0 +1,188 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.authentication;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Priority;
+import javax.ws.rs.NotAuthorizedException;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerRequestFilter;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.apache.cxf.common.security.SimplePrincipal;
+import org.apache.cxf.jaxws.context.WrappedMessageContext;
+import org.apache.cxf.message.Message;
+import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer;
+import org.apache.cxf.rs.security.jose.jwt.JwtToken;
+import org.apache.cxf.sts.QNameConstants;
+import org.apache.cxf.sts.STSConstants;
+import org.apache.cxf.sts.operation.TokenValidateOperation;
+import org.apache.cxf.sts.rest.impl.TokenUtils;
+import org.apache.cxf.ws.security.sts.provider.model.RequestSecurityTokenResponseType;
+import org.apache.cxf.ws.security.sts.provider.model.StatusType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION;
+import static javax.ws.rs.core.Response.Status.UNAUTHORIZED;
+import static javax.ws.rs.core.Response.status;
+import static org.apache.cxf.common.util.StringUtils.isEmpty;
+import static org.apache.cxf.jaxrs.utils.JAXRSUtils.getCurrentMessage;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.createValidateRequestSecurityTokenType;
+import static org.apache.cxf.sts.rest.impl.TokenUtils.getEncodedJwtToken;
+
+@PreMatching
+@Priority(AUTHENTICATION)
+public class JwtAuthenticationFilter implements ContainerRequestFilter {
+    private static final Logger LOG = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+    private static final QName QNAME_WST_STATUS =
+            QNameConstants.WS_TRUST_FACTORY.createStatus(null).getName();
+    private static final String AUTHENTICATION_SCHEME = "Bearer";
+    private TokenValidateOperation validateOperation;
+
+    @Override
+    public void filter(ContainerRequestContext context) throws IOException {
+        if (context.getSecurityContext().getUserPrincipal() != null) {
+            LOG.debug("User principal is already set, pass filter without authentication processing");
+            return;
+        }
+
+        final String authorizationHeader  = context.getHeaderString(AUTHORIZATION);
+        if (isEmpty(authorizationHeader)) {
+            return; //abortUnauthorized(context, "This is no Authorization header in the request");
+        }
+
+        if (!isTokenBasedAuthentication(authorizationHeader)) {
+            abortUnauthorized(context, "Authorization header is present, but is not Bearer schema");
+        }
+
+        if (ofNullable(validateOperation).isPresent()) {
+            try {
+                validateToken(context, authorizationHeader);
+            } catch (Exception e) {
+                abortUnauthorized(context, e.getMessage());
+            }
+        } else {
+            LOG.debug("ValidationOperation bean is not defined");
+            abortUnauthorized(context, null);
+        }
+    }
+
+    private boolean isTokenBasedAuthentication(String authorizationHeader) {
+        return !isEmpty(authorizationHeader)
+            && authorizationHeader.toLowerCase().startsWith(AUTHENTICATION_SCHEME.toLowerCase() + " ");
+    }
+
+    private void abortUnauthorized(final ContainerRequestContext context, final String message) {
+        LOG.error("Authorization error: {}", message);
+        context.abortWith(status(UNAUTHORIZED).build());
+    }
+
+    private void validateToken(final ContainerRequestContext context, final String authorizationHeader)
+        throws Exception {
+        final String tokenString = getEncodedJwtToken(authorizationHeader);
+        if (isEmpty(tokenString)) {
+            throw new NotAuthorizedException("Bearer schema is present but token is empty",
+                TokenUtils.BEARER_AUTH_SCHEMA);
+        }
+
+        final Message message = getCurrentMessage();
+        final RequestSecurityTokenResponseType response = validateOperation.validate(
+            createValidateRequestSecurityTokenType(tokenString, "jwt"), null,
+            new WrappedMessageContext(message));
+        if (validateResponse(response)) {
+            createSecurityContext(context, tokenString);
+        } else {
+            throw new NotAuthorizedException("Bearer token validation is failed", TokenUtils.BEARER_AUTH_SCHEMA);
+        }
+    }
+
+    private void createSecurityContext(final ContainerRequestContext context, final String token) {
+        final JwsJwtCompactConsumer jwtConsumer = new JwsJwtCompactConsumer(token);
+        final JwtToken jwt = jwtConsumer.getJwtToken();
+        final SecurityContext securityContext = context.getSecurityContext();
+        final SecurityContext newSecurityContext = new SecurityContext() {
+
+            public Principal getUserPrincipal() {
+                return ofNullable(jwt)
+                        .map(j -> j.getClaims())
+                        .map(c -> c.getSubject())
+                        .map(SimplePrincipal::new)
+                        .orElse(null);
+            }
+
+            public boolean isUserInRole(String role) {
+                List<String> roles = (List<String>) ofNullable(jwt)
+                        .map(j -> j.getClaims())
+                        .map(c -> c.getClaim("roles"))

Review comment:
       "roles" here should be configurable.

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/api/RealmSecurityTokenService.java
##########
@@ -0,0 +1,251 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.api;
+
+import java.util.List;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
+
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
+import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
+import io.swagger.v3.oas.annotations.info.Info;
+import io.swagger.v3.oas.annotations.parameters.RequestBody;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.security.SecurityScheme;
+
+@OpenAPIDefinition(
+    info = @Info(title = "SecurityTokenService REST interface", version = "1")
+)
+@SecurityScheme(description = "The JWT token",
+    in = SecuritySchemeIn.HEADER,
+    type = SecuritySchemeType.HTTP,
+    scheme = "Bearer",
+    bearerFormat = "JWT")
+/**
+ * Here possible to declare roles that should have access to the endpoints
+ * @DeclareRoles({"user", "admin"})
+ */
+@Path("/{realm}")
+public interface RealmSecurityTokenService {
+
+    @GET
+    @Path("/token/{tokenType}")
+    @Produces(MediaType.APPLICATION_XML)
+    @Operation(
+        summary = "Return XML token according requested token type and key type",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return requested token"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response getXMLToken(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @PathParam("tokenType")
+        @Parameter(name = "tokenType", description = "Type of token", required = true, example = "saml")
+        String tokenType,
+        @QueryParam("keyType")
+        @Parameter(name = "keyType", description = "Type of key", required = true, example = "PublicKey")
+        String keyType,
+        @QueryParam("claim")
+        @Parameter(name = "claims", description = "List of claim for requested token", example = "roles, emailaddress")
+        List<String> claims,
+        @QueryParam("appliesTo")
+        @Parameter(name = "appliesTo", description = "URL of requested token audience",
+            example = "https://localhost:8443/test")
+        String appliesTo,
+        @QueryParam("wstrustResponse")
+        @Parameter(name = "wstrustResponse", description = "Flag that shows that if response wstrust or now",
+            example = "false")
+        @DefaultValue("false")
+        boolean wstrustResponse);
+
+    @GET
+    @Path("/token/{tokenType}")
+    @Produces(MediaType.APPLICATION_JSON)
+    @Operation(
+        summary = "Return JWT token in JSON according requested token type and key type",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return requested token"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response getJSONToken(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @PathParam("tokenType")
+        @Parameter(name = "tokenType", description = "Type of token", required = true, example = "jwt")
+        String tokenType,
+        @QueryParam("keyType")
+        @Parameter(name = "keyType", description = "Type of key", required = true, example = "Bearer")
+        String keyType,
+        @QueryParam("claim")
+        @Parameter(name = "claims", description = "List of claim for requested token", example = "roles, emailaddress")
+        List<String> claims,
+        @QueryParam("appliesTo")
+        @Parameter(name = "appliesTo", description = "URL of requested token audience",
+            example = "https://localhost:8443/test")
+        String appliesTo);
+
+    @GET
+    @Path("/token/{tokenType}")
+    @Produces(MediaType.TEXT_PLAIN)
+    @Operation(
+        summary = "Return JWT token in plain text format according requested token type and key type",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return requested token"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response getPlainToken(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @PathParam("tokenType")
+        @Parameter(name = "tokenType", description = "Type of token", required = true, example = "jwt")
+        String tokenType,
+        @QueryParam("keyType")
+        @Parameter(name = "keyType", description = "Type of key", required = true, example = "Bearer")
+        String keyType,
+        @QueryParam("claim")
+        @Parameter(name = "claims", description = "List of claim for requested token", example = "roles, emailaddress")
+        List<String> claims,
+        @QueryParam("appliesTo")
+        @Parameter(name = "appliesTo", description = "URL of requested token audience",
+            example = "https://localhost:8443/test")
+        String appliesTo);
+
+    @POST
+    @Path("/token")
+    @Consumes({"application/xml", "application/json"})
+    @Produces({"application/xml", "application/json"})
+    @Operation(
+        summary = "Return token according requested token type and key type",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return requested token"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response getToken(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @RequestBody(description = "Object GetTokenRequest describes requested token parameters", required = true)
+        GetTokenRequest request);
+
+    @POST
+    @Path("/token/validate")
+    @Produces({"application/xml", "application/json"})
+    @Operation(
+        summary = "Validate incoming token",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return result of validation"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response validate(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @RequestBody(description = "Object TokenRequest represents token for validation", required = true)
+        TokenRequest request);
+
+    @POST
+    @Path("/token/renew")
+    @Produces({"application/xml", "application/json"})
+    @Operation(
+        summary = "Renew incoming token",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return new token"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response renew(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @RequestBody(description = "Object TokenRequest represents token for renewal", required = true)
+        TokenRequest request);
+
+    @DELETE
+    @Path("/token")
+    @Produces({"application/xml", "application/json"})
+    @Operation(
+        summary = "Cancel incoming token",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Token is cancelled"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response remove(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @RequestBody(description = "Object TokenRequest represents token for canceling", required = true)
+        TokenRequest request);
+
+    @POST
+    @Path("/token/exchange")
+    @Produces({"application/xml", "application/json"})
+    @Operation(
+        summary = "Return exchange token according requested token type and key type",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return requested token"),
+            @ApiResponse(responseCode = "401", description = "You are not authorized to access the endpoint")
+        }
+    )
+    Response getKeyExchangeToken(
+        @PathParam("realm")
+        @Parameter(name = "realm", description = "Name of realm", required = true)
+        String realm,
+        @RequestBody(description = "Object GetTokenRequest describes requested access token parameters",
+            required = true)
+        GetTokenRequest request);
+
+    @GET
+    @Path("/jwk/keys")
+    @Produces({"application/json"})
+    @Operation(
+        summary = "Return JWK public key for requested realm",
+        responses = {
+            @ApiResponse(responseCode = "200", description = "Return requested public JWK"),
+            @ApiResponse(responseCode = "500",
+                description = "In case wrong configuration of RS security for requested realm")
+        }
+    )
+    JsonWebKeys getPublicVerificationKeys();

Review comment:
       Is this method tested anywhere? I don't see it...

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/api/BaseResponse.java
##########
@@ -0,0 +1,80 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.api;

Review comment:
       The package name of "org.apache.cxf.sts.rest" is a bit problematic in OSGi, as it clashes with the module in STS Core. Can we rename this to something different?

##########
File path: services/sts/sts-rest/src/main/java/org/apache/cxf/sts/rest/authentication/STSJaasAuthenticationFilter.java
##########
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.sts.rest.authentication;
+
+import java.util.Map;
+import java.util.Optional;
+
+import javax.annotation.Priority;
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.PreMatching;
+import javax.ws.rs.core.SecurityContext;
+
+import org.apache.cxf.jaxrs.security.JAASAuthenticationFilter;
+import org.apache.cxf.sts.rest.token.realm.ExtRealmProperties;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Optional.ofNullable;
+import static javax.ws.rs.Priorities.AUTHENTICATION;
+import static javax.ws.rs.core.SecurityContext.BASIC_AUTH;
+import static org.apache.cxf.jaxrs.utils.JAXRSUtils.getCurrentMessage;
+import static org.apache.cxf.sts.rest.impl.RealmSecurityConfigurationFilter.REALM_NAME_PARAM;
+
+@PreMatching
+@Priority(AUTHENTICATION)
+public class STSJaasAuthenticationFilter extends JAASAuthenticationFilter {

Review comment:
       Please add some Javadocs explaining what the class does and how to use it. Same goes for JAAS_CONTEXT_NAME_PARAM below.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[hidden email]