Thank you very much Joao... lets see if i can somehow connect this with karaf jaas service
br, Matthias Am Mo., 27. Juni 2022 um 17:40 Uhr schrieb João Assunção < [email protected]>: > Hello Matthias. > > Sorry but I don't have a public repo with an example. > I had some issues in the past when using more recent versions of Aries JAX > RS due to conflicts with the CXF version I was using in another service, so > I kept using version 1.0.4. > In my latest projects, where I needed more customization, I ditched Aries > JAX RS and used CXF directly. Needed more code and had to use > JAXRSServerFactoryBean to create the server. Also lost the possibility to > use service injection :-( > > The code for JWT is mostly taken from jose4j examples: > > @Component(service = JwtService.class, // >> immediate = true, // >> scope = ServiceScope.SINGLETON) >> public class JwtService { >> >> private static final Logger LOGGER = >> LoggerFactory.getLogger(JwtService.class); >> >> // time when the token will expire (minutes) >> private static final float TOKEN_TTL = 30; >> >> public static final String ISSUER = "ACME"; >> >> public static final String AUDIENCE = "universe"; >> >> public static final String CLAIM_NAME = "name"; >> >> public static final String CLAIM_PERMISSIONS = "permissions"; >> >> private RsaJsonWebKey rsaJsonWebKey; >> >> private JwtConsumer jwtConsumer; >> >> @Activate >> public void activate() throws JoseException { >> >> initWebKey(); >> initJWTconsumer(); >> } >> >> private void initWebKey() throws JoseException { >> >> rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048); >> rsaJsonWebKey.setKeyId("keyId"); >> >> } >> >> private void initJWTconsumer() { >> >> jwtConsumer = new JwtConsumerBuilder() >> .setRequireExpirationTime() // the JWT must have an >> expiration >> // time >> .setAllowedClockSkewInSeconds(30) // allow some leeway in >> // validating time based >> // claims to account >> for clock >> // skew >> .setRequireSubject() // the JWT must have a subject claim >> .setExpectedIssuer(ISSUER) // whom the JWT needs to have >> been >> // issued by >> .setExpectedAudience(AUDIENCE) // to whom the JWT is >> intended >> // for >> .setVerificationKey(rsaJsonWebKey.getKey()) // verify the >> // signature >> with >> // the public >> key >> .setJwsAlgorithmConstraints( // only allow the expected >> // signature algorithm(s) in >> the >> // given context >> new >> AlgorithmConstraints(ConstraintType.WHITELIST, // which >> >> // is >> >> // only >> >> // RS256 >> >> // here >> AlgorithmIdentifiers.RSA_USING_SHA256)) >> .build(); // create the JwtConsumer instance >> >> } >> >> public String createToken(SubjectDetails subjectDetails) throws >> AuthenticationException { >> >> // Create the Claims, which will be the content of the JWT >> JwtClaims claims = new JwtClaims(); >> claims.setIssuer(ISSUER); // who creates the token and signs it >> claims.setAudience(AUDIENCE); // to whom the token is intended to >> be >> // sent >> claims.setExpirationTimeMinutesInTheFuture(TOKEN_TTL); >> claims.setGeneratedJwtId(); // a unique identifier for the token >> claims.setIssuedAtToNow(); // when the token was issued/created >> (now) >> claims.setNotBeforeMinutesInThePast(2); >> claims.setSubject(subjectDetails.getId()); >> claims.setStringClaim(CLAIM_NAME, subjectDetails.getName()); >> claims.setStringListClaim(CLAIM_PERMISSIONS, >> subjectDetails.getPermissionsList()); >> >> // A JWT is a JWS and/or a JWE with JSON claims as the payload. >> // In this example it is a JWS so we create a JsonWebSignature >> object. >> JsonWebSignature jws = new JsonWebSignature(); >> >> // The payload of the JWS is JSON content of the JWT Claims >> jws.setPayload(claims.toJson()); >> >> // The JWT is signed using the private key >> jws.setKey(rsaJsonWebKey.getPrivateKey()); >> >> jws.setKeyIdHeaderValue(rsaJsonWebKey.getKeyId()); >> >> // Set the signature algorithm on the JWT/JWS that will integrity >> // protect the claims >> >> jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); >> >> try { >> return jws.getCompactSerialization(); >> } >> catch (JoseException ex) { >> throw new AuthenticationException("Token creation failed", >> ex); >> } >> } >> >> public SubjectDetails validateToken(String token) throws >> AuthenticationException { >> >> try { >> JwtClaims jwtClaims = jwtConsumer.processToClaims(token); >> String subjectName = >> jwtClaims.getStringClaimValue(CLAIM_NAME); >> List<String> permissions = >> jwtClaims.getStringListClaimValue(CLAIM_PERMISSIONS); >> Set<Permission> permissionsSet = >> Permission.fromList(permissions); >> permissionsSet.add(Permission.AUTHENTICATED); // Implicit >> permission >> return new SubjectDetails(jwtClaims.getSubject(), >> subjectName, permissionsSet); >> } >> catch (InvalidJwtException ex) { >> logException("InvalidJwtException", ex); >> throw new AuthenticationException(ex.getMessage()); >> >> } >> catch (MalformedClaimException ex) { >> logException("MalformedClaimException", ex); >> throw new AuthenticationException(ex.getMessage()); >> } >> >> } >> >> protected static void logException(String msg, Exception ex) { >> >> LOGGER.debug(msg, ex); >> } >> } >> > > > > João Assunção > > Email: [email protected] > Mobile: +351 916968984 > Phone: +351 211933149 > Web: www.exploitsys.com > > > > > On Mon, Jun 27, 2022 at 9:33 AM Matthias Leinweber < > [email protected]> wrote: > >> Hello, >> >> I am using karaf 4.3.7 and yes I saw the examples in karaf. The version >> used is 1.0.6 which is pretty old. I tried to update to 1.0.10 but i dont >> see what is going wrong. There is the Application, Servlet, and Whiteboard. >> But the servlet is not accessible. >> With version 2.0.1 is cxf-core required. That would also be fine. >> Unfortunately it does not work without a url prefix which would require a >> change on the client side. >> >> @ João thank you very much for your help. Do you also have a repo link to >> a working example; where does the JWTService come from? >> >> Thank >> br, >> Matthias >> >> Am Do., 23. Juni 2022 um 11:02 Uhr schrieb João Assunção < >> [email protected]>: >> >>> Hello Matthias, >>> >>> Regarding authentication I normally use a ContainerRequestFilter. >>> Follows an example of a filter where authentication is done using a JWT >>> token. >>> It uses JwtService that is responsible for creating and validating >>> tokens. It uses jose4j. >>> >>> >>> @Secured >>>> @Provider >>>> @Priority(Priorities.AUTHENTICATION) >>>> @Component(scope = ServiceScope.PROTOTYPE, // >>>> service = { ContainerRequestFilter.class }, // >>>> property = { >>>> JaxrsWhiteboardConstants.JAX_RS_EXTENSION + "=" + true, >>>> // >>>> JaxrsWhiteboardConstants.JAX_RS_NAME + "=" + >>>> "TokenAuthenticationFilter", // >>>> Constants.JAX_RS_APPLICATION_SELECT, // >>>> }) >>>> public class TokenAuthenticationFilter implements >>>> ContainerRequestFilter { >>>> >>>> private static final Logger LOGGER = >>>> LoggerFactory.getLogger(TokenAuthenticationFilter.class); >>>> >>>> private static final String AUTHENTICATION_SCHEME = "Bearer"; >>>> >>>> private static final String AUTHENTICATION_SCHEME_PREFIX = >>>> AUTHENTICATION_SCHEME + " "; >>>> >>>> @Reference(cardinality = ReferenceCardinality.MANDATORY) >>>> JwtService jwtService; >>>> >>>> @Context >>>> private ResourceInfo resourceInfo; >>>> >>>> @Override >>>> public void filter(ContainerRequestContext requestContext) throws >>>> IOException { >>>> >>>> // Get the Authorization header from the request >>>> String authorizationHeader = >>>> requestContext.getHeaderString(HttpHeaders.AUTHORIZATION); >>>> LOGGER.debug("authorizationHeader {}", authorizationHeader); >>>> // Validate the Authorization header >>>> if (!isTokenBasedAuthentication(authorizationHeader)) { >>>> LOGGER.info("Missing auth token. Aborting"); >>>> abortWithUnauthorized(requestContext); >>>> return; >>>> } >>>> // Extract the token from the Authorization header >>>> final String token = >>>> authorizationHeader.substring(AUTHENTICATION_SCHEME_PREFIX.length()); >>>> try { >>>> // Validate the token. An exception will be thrown if >>>> invalid >>>> SubjectDetails subjectDetails = >>>> jwtService.validateToken(token); >>>> LOGGER.debug("Token for subject {} accepted", >>>> subjectDetails.getId()); >>>> initSecurityContext(requestContext, subjectDetails); >>>> Permission requiredPermission = >>>> getPermissionForResource(resourceInfo); >>>> if (!subjectDetails.hasPermission(requiredPermission)) { >>>> LOGGER.debug("subject {} lack permission {}", >>>> subjectDetails.getId(), requiredPermission); >>>> abortWithForbidden(requestContext); >>>> } >>>> } >>>> catch (AuthenticationException e) { >>>> LOGGER.trace("Ignored", e); >>>> LOGGER.info("Token validation failed. Aborting"); >>>> abortWithUnauthorized(requestContext); >>>> } >>>> } >>>> >>>> /** >>>> * @param requestContext >>>> * @param subjectDetails >>>> */ >>>> private void initSecurityContext(ContainerRequestContext >>>> requestContext, SubjectDetails subjectDetails) { >>>> >>>> final SecurityContext currentSecurityContext = >>>> requestContext.getSecurityContext(); >>>> boolean secure = currentSecurityContext.isSecure(); >>>> requestContext.setSecurityContext(new >>>> InternalSecurityContext(subjectDetails, secure)); >>>> >>>> } >>>> >>>> private boolean isTokenBasedAuthentication(String >>>> authorizationHeader) { >>>> >>>> // Check if the Authorization header is valid >>>> // It must not be null and must be prefixed with "Bearer" plus a >>>> // whitespace >>>> // The authentication scheme comparison must be case-insensitive >>>> return StringUtils.startsWithIgnoreCase(authorizationHeader, >>>> AUTHENTICATION_SCHEME_PREFIX); >>>> } >>>> >>>> private void abortWithUnauthorized(ContainerRequestContext >>>> requestContext) { >>>> >>>> // Abort the filter chain with a 401 status code response >>>> // The WWW-Authenticate header is sent along with the response >>>> requestContext.abortWith( >>>> Response.status(Response.Status.UNAUTHORIZED) >>>> .header(HttpHeaders.WWW_AUTHENTICATE, >>>> AUTHENTICATION_SCHEME + " realm=\"" + >>>> Constants.APP_NAME + "\"") >>>> .build()); >>>> } >>>> >>>> private void abortWithForbidden(ContainerRequestContext >>>> requestContext) { >>>> >>>> >>>> requestContext.abortWith(Response.status(Response.Status.FORBIDDEN).build()); >>>> } >>>> >>>> static Permission getPermissionForResource(ResourceInfo >>>> resourceInfo) { >>>> >>>> Permission permission = >>>> extractPermission(resourceInfo.getResourceMethod()); >>>> if (permission != null) { >>>> return permission; >>>> } >>>> permission = extractPermission(resourceInfo.getResourceClass()); >>>> return permission; >>>> >>>> } >>>> >>>> // Extract Permission from the annotated element >>>> static Permission extractPermission(AnnotatedElement >>>> annotatedElement) { >>>> >>>> if (annotatedElement == null) { >>>> return null; >>>> } >>>> Secured secured = annotatedElement.getAnnotation(Secured.class); >>>> return secured == null ? null : secured.value(); >>>> } >>>> >>>> private static class InternalSecurityContext implements >>>> SecurityContext { >>>> >>>> private final boolean secure; >>>> >>>> private final SubjectDetails subjectDetails; >>>> >>>> InternalSecurityContext(SubjectDetails subjectDetails, boolean >>>> secure) { >>>> >>>> this.subjectDetails = subjectDetails; >>>> this.secure = secure; >>>> } >>>> >>>> @Override >>>> public Principal getUserPrincipal() { >>>> >>>> return subjectDetails; >>>> } >>>> >>>> @Override >>>> public boolean isUserInRole(String role) { >>>> >>>> // We are not using role base authorization >>>> return true; >>>> } >>>> >>>> @Override >>>> public boolean isSecure() { >>>> >>>> return secure; >>>> } >>>> >>>> @Override >>>> public String getAuthenticationScheme() { >>>> >>>> return AUTHENTICATION_SCHEME; >>>> } >>>> >>>> } >>>> >>>> /** >>>> * For test purposes >>>> * >>>> * @param jwtService >>>> * @return >>>> */ >>>> public static TokenAuthenticationFilter >>>> createTestInstance(JwtService jwtService) { >>>> >>>> TokenAuthenticationFilter filter = new >>>> TokenAuthenticationFilter(); >>>> filter.jwtService = jwtService; >>>> return filter; >>>> } >>>> } >>>> >>> >>> The Secured annotation is used to mark the methods that need to be >>> protected and the required permission. >>> >>> @NameBinding >>>> @Retention(RetentionPolicy.RUNTIME) >>>> @Target({ ElementType.TYPE, ElementType.METHOD }) >>>> public @interface Secured { >>>> >>>> Permission value() default Permission.AUTHENTICATED; >>>> } >>>> >>> >>> João Assunção >>> >>> Email: [email protected] >>> Mobile: +351 916968984 >>> Phone: +351 211933149 >>> Web: www.exploitsys.com >>> >>> >>> >>> >>> On Wed, Jun 22, 2022 at 8:54 PM Matthias Leinweber < >>> [email protected]> wrote: >>> >>>> Hello Karaf user, >>>> >>>> i try to get aries-jax-rs-whiteboard in a version > 1.0.6 running >>>> without success. 2.0.1 with installed cxf is working fine. But in >>>> 1.0.7-1.0.10. http:list does not show a servlet and my jaxrs whiteboard >>>> resources are not working. >>>> >>>> Any hints which component needs to be installed? Reading BNDrun + >>>> Components DSL is pretty confusing. >>>> >>>> Btw, does anyone have a good example for authentication? Shiro seems to >>>> be a bit overkill. >>>> >>>> br, >>>> Matthias >>>> >>> >> >> >>
