Following the documentation here and there, I managed to setup an Authorization Server that gives out JWT access tokens signed with asymmetric key, which are verified locally by a Resource Server using a local copy of the public key. So far so good.
My final goal is for Resource Servers to use the JWKS endpoint on the Authorization Server, and use the 'kid' header in the JWT to lookup the right key in the JWKS and verify locally, supporting key rotation.
I've found how to make the Authorization Server expose a JWKS endpoint, and also how to specify the key-set-uri for the resource server.
However, it seems that there is no way to
publish kid (key id) values in the JWKS
include the kid header in the JWT
Is there a way to do this?
I found a way to set the kid in jwks endpoint:
#FrameworkEndpoint
public class JwkSetEndpoint {
private final KeyPair keyPair;
public JwkSetEndpoint(KeyPair keyPair) {
this.keyPair = keyPair;
}
#GetMapping("/.well-known/jwks.json")
#ResponseBody
public Map<String, Object> getKey() {
RSAPublicKey publicKey = (RSAPublicKey) this.keyPair.getPublic();
RSAKey key = new RSAKey.Builder(publicKey)
.keyID("YOUR_KID_HERE")
.keyUse(KeyUse.SIGNATURE).build();
return new JWKSet(key).toJSONObject();
}
}
What I did not find was a way to set it in the header of JWT.
While having the same problem I stumbled upon this post. So i hope it will be useful to someone. I do not think this is the best solution, so maybe some one comes up with a better answer i hope like setting some external bean for example.
Background:
The Jwk store is comparing the KID in the token header with the one in memory if not available it will request the well-known endpoint
So putting the KID in the JwkSetEndpoint will result in a json file with the kid inside.
next to this you need to get the KID on the header of the jwt token.
my solution in my class which extends JwtAccessTokenConverter
#Override
protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String content = null;
try {
content = objectMapper.formatMap(getAccessTokenConverter().convertAccessToken(accessToken, authentication));
} catch (Exception e) {
throw new IllegalStateException("Cannot convert access token to JSON", e);
}
Map<String, String> headers = getJwtHeader();
String token = JwtHelper.encode(content, signer, headers).getEncoded();
return token;
}
next to the KID header the Tokenstore expects a use header set to signing.
i also had to override the signer object because i got stuck with a hmac signer instead of the desired RsaSigner.
Related
This is my code.
The clientService implements ClientDetailsService , but not found the token storage.
How to storage and get token when custom(e.g. database) ClientDetailsService?
When we have a custom implementation of ClientDetailsService, we essentially override its loadClientByClientId(..) method. This method takes clientId in parameter i.e. the username of the client. In that custom implementation class, all we need to do is check if the given client name exists in database or not. If it does exist, then load all its data and return the object. This class needs be injected with DAO or Repository's dependency to talk to database.
#Override
public ClientDetails loadClientByClientId(final String clientId) throws ClientRegistrationException {
Objects.requireNonNull(clientId, "Client ID must not be null");
final com.ex.auth.domain.ClientDetails clientDetails = clientDetailsRepository.findOne(clientId);
if (clientDetails == null) {
throw new NoSuchClientException(String.format("Client %s does not exist.", clientId));
}
return convertToDmo(clientDetails);
}
Here is an excerpt from Picketlink Federation 2.5.4 SP4 class org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler which shipped with Jboss 6.4 (no tag on picketlink.org github for 2.5.4 SP4?). My problem is that the top line, request.getRequestDocument() returns null. The object itself, request (SAML2HandlerRequest) looks fine (not null):
public void handleStatusResponseType(SAML2HandlerRequest request, SAML2HandlerResponse response)
throws ProcessingException {
HTTPContext httpContext = (HTTPContext) request.getContext();
ResponseType responseType = (ResponseType) request.getSAML2Object();
(...)
Document responseDocument = request.getRequestDocument();
Element assertionElement =
DocumentUtil.getChildElement(responseDocument.getDocumentElement(),
new QName(JBossSAMLConstants.ASSERTION.get()));
if (assertionElement != null) {
try {
Document assertionDocument = DocumentUtil.createDocument();
Node clonedAssertion = assertionElement.cloneNode(true);
assertionDocument.adoptNode(clonedAssertion);
assertionDocument.appendChild(clonedAssertion);
String assertionAttributeName = (String) handlerConfig
.getParameter(GeneralConstants.ASSERTION_SESSION_ATTRIBUTE_NAME);
if (assertionAttributeName != null) {
session.setAttribute(assertionAttributeName, assertionDocument);
}
session.setAttribute(GeneralConstants.ASSERTION_SESSION_ATTRIBUTE_NAME, assertionDocument);
} catch (ConfigurationException e) {
throw new ProcessingException("Could not store assertion document into session.", e);
}
}
FYI, the reason I need this document is is that as you see in the code, if the responseDocument is null, then the asserionElement is null. And if the assertionElement is null, then my SAML token will not be in the HTTPSession under the key given, and that is what I need.
The identity provider is PingIdentity. And tips or tricks on what could be going on here before I have to learn the whole PicketLink code base?
Thanks!
FYI my set up is jboss 6.4, with the picketlink it ships with (2.5.4 SP4), with PingIdentity for SAML tokens (IdP). FYI, all is working in an SSO scenario from WebApp 1 to WebApp 2, the only thing is that I need to get the SAML token from the SSO session on WebApp 2....
Got it ))), the PingIdentity IdP SP Connection, Browser SSO, Protocol Settings, I had "Encrypt the Entire Assertion". Setting that to "None", fixes it and I get my token in the HTTP Session. So... ...with PKI, we encrypt with public key (cert) and decrypt with private key (key). So either picketlink did not have the right private key (but I think it did) or the two systems don't understand how to do this PKI encrypt/decypt...
I am new to Swagger.
I am using Swagger UI to generate swagger documentation. I have two API calls. First call is to generate token based on user name and password. Second call needs token generated by first call.
How I set that token for second call using Swagger UI?
#ApiImplicitParams and #ApiImplicitParam should do the trick:
#GET
#Produces("application/json")
#ApiImplicitParams({
#ApiImplicitParam(name = "Authorization", value = "Authorization token",
required = true, dataType = "string", paramType = "header") })
public String getUser(#PathParam("username") String userName) {
...
}
From the documentation:
You may wish you describe operation parameters manually. This can be for various reasons, for example:
Using Servlets which don't use JAX-RS annotations.
Wanting to hide a parameter as it is defined and override it with a completely different definition.
Describe a parameter that is used by a filter or another resource prior to reaching the JAX-RS implementation.
The Swagger UI will be updated so you can send your token from there. No changes to HTML will be necessary.
Note: A while ago, when documenting a REST API with Swagger, I realized that just adding #ApiImplicitParam is not enough (even if you have only one parameter). Anyway, you must add #ApiImplicitParams too.
My configuration for 2.9.2 Swagger version to add Authorization on Swagger UI and send the Bearer token
#Configuration
public class SwaggerConfiguration{
//...
#Bean
public Docket api(ServletContext servletContext) {
return new Docket(DocumentationType.SWAGGER_2)...
.securitySchemes(Arrays.asList(apiKey()))
.securityContexts(Collections.singletonList(securityContext()));
}
private SecurityContext securityContext() {
return SecurityContext.builder().securityReferences(defaultAuth()).forPaths(PathSelectors.regex("/.*")).build();
}
private List<SecurityReference> defaultAuth() {
final AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
final AuthorizationScope[] authorizationScopes = new AuthorizationScope[]{authorizationScope};
return Collections.singletonList(new SecurityReference("Bearer", authorizationScopes));
}
private ApiKey apiKey() {
return new ApiKey("Bearer", "Authorization", "header");
}
}
Another option is to add globalOperationParameters. It will add a field for authorization in every endpoint.
Define authorization header parameter:
Parameter authHeader = new ParameterBuilder()
.parameterType("header")
.name("Authorization")
.modelRef(new ModelRef("string"))
.build();
Add it to Docket configuration:
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(...)
.paths(...)
.build()
.apiInfo(...)
.globalOperationParameters(Collections.singletonList(authHeader));
And it will look like this:
There is a hack that might work by using responseInterceptor and requestInterceptor
First capture response of the the first API call using responseInterceptor and save the token (in the example in local storage), then use requestInterceptor to add the Authorization header with the saved token.
const ui = SwaggerUIBundle({
...
responseInterceptor:
function (response) {
if (response.obj.access_token) {
console.log(response.obj.access_token)
const token = response.obj.access_token;
localStorage.setItem("token", token)
}
return response;
},
requestInterceptor:
function (request) {
console.log('[Swagger] intercept try-it-out request');
request.headers.Authorization = "Bearer " + localStorage.getItem("token");
return request;
}
}
You would have to customise the swagger index page to accomplish that I believe.
You can make the input 'input_apiKey' hidden and add two inputs for username and password. Then you make an ajax call to update the hidden input with your token.
This is an old question but this is how I solved it recently with version 2.7.0 for my JWT tokens
In your swagger configuration, add below SecurityConfiguration bean. Important part being leaving fifth argument empty or null.
#Bean
public SecurityConfiguration securityInfo() {
return new SecurityConfiguration(null, null, null, null, "", ApiKeyVehicle.HEADER,"Authorization","");
}
Add securitySchemes(Lists.newArrayList(apiKey())) to your main Docket bean.
#Bean
public Docket docket()
{
return new Docket(DocumentationType.SWAGGER_2).select()
.....build().apiInfo(...).securitySchemes(Lists.newArrayList(apiKey()));
}
private ApiKey apiKey() {
return new ApiKey("Authorization", "Authorization", "header");
}
Then in UI , you need to click on Authorize button and input "Bearer access_token" (for Authorization text box )where access_token is token provided by jWT token server.
Once this authorization is saved,that will become effective for all end points. Adding a separate text field for each end point looks very cumbersome.
Using SpringDoc with the springdoc-openapi-maven-plugin my option is to use a SwaggerConfig.Java:
#Configuration
public class SwaggerConfiguration {
#Bean
public OpenAPI customOpenAPI(#Value("${project.version}") String appVersion) {
OpenAPI openApi = new OpenAPI();
openApi.info(
new Info()
.title("Title Example")
.version(appVersion)
.description("Swagger server created using springdocs - a library for OpenAPI 3 with spring boot.")
);
openApi.components(
new Components().addSecuritySchemes("bearer-jwt",
new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT")
.in(SecurityScheme.In.HEADER).name("Authorization"))
);
openApi.addSecurityItem(
new SecurityRequirement().addList("bearer-jwt", Arrays.asList("read", "write"))
);
return openApi;
}
}
I have REST service requirements in which some calls require authentication and some don't. Absolutely no state is used, as the calls are all independent from one another. I have put something together which seems to work, but is this the right way to go about not using sessions?
This question is kind of related to my WCF question which is answered here.
Firstly I registered the authentication method:
Plugins.Add(new AuthFeature(() => new AuthUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
}
));
I then attribute the respective calls (or service or DTO) with the Authenticate attribute:
[Authenticate]
public HelloResponse Post(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name + " with POST & Auth"};
}
I inherit from the BasicAuthProvider class which does the authentication:
public class CustomCredentialsAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return userName == "dylan" && password == "abc123";
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
session.IsAuthenticated = true;
//Important: You need to save the session!
authService.SaveSession(session, new TimeSpan(0,0,10));
}
}
As you can see, I do save the session but it times out after 10 seconds. This is the part that I'm sure can potentially be done better. It seems to work nicely though.
Is there a better way of doing what I'm trying to accomplish?
Is there also any way, due to the sessionless nature of these services, to remove the Auth, AssignRoles and UnassignRoles methods?
If you wanted to keep using ServiceStack's Authentication and Session support you could just add a response filter that clears the user session after the service is executed, e.g:
this.ResponseFilters.Add((req, res, dto) => req.RemoveSession());
Basically after each request is executed it clears the session, so no record of them having authenticated exists.
Otherwise you can just skip using ServiceStack's Authentication completely and just provide your own via RequestFitlers of FilterAttributes (which is essentially what SS Auth does under the hood).
I'm new to this topic so sorry if this is a stupid question :\
I'm trying to decrypt a message with a given public key. Both the message and public key is given from the webservice.
See the following code for how i currently do the decrypting:
for (NSValue *refVal in keyRefs) {
SecKeyRef p_key = NULL;
[refVal getValue:&p_key];
if (p_key == NULL) continue;
size_t dataLength = encryptedData.length;
size_t outPutLength = MAX(dataLength, SecKeyGetBlockSize(p_key));
void *outPutBuf = malloc(outPutLength);
if (outPutBuf) {
// Error handling
OSStatus status = SecKeyDecrypt(p_key,
kSecPaddingNone,
encryptedData.bytes,
encryptedData.length,
outPutBuf,
&outPutLength
);
NSLog(#"decryption result code: %ld (size: %lu)", status, outPutLength);
NSLog(#"FINAL decrypted text: %s", outPutBuf);
if (status == errSecSuccess) {
break;
}
} else {
//Error handling
}
}
I get no errors, but the decrypted string is displayed like this (the correct output should be a JSON array):
decryption result code: 0 size:511)
FINAL decrypted text: ˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇˇ
Is it because I use the "SecKeyDecrypt" with a "public key" instead of a "private key"? In that case, what should i instead use for decrypting?
Thanks for any help regarding this!
EDIT: I'm using code from: http://blog.flirble.org/2011/01/05/rsa-public-key-openssl-ios/ to use the public key i get from the server (this is where "keyRefs" from the code snippet comes from)
Of course, the public key is something someone else uses to encrypt data so that only someone with the private key can decrypt it.
The very definition of a public key is that you can give it to anyone. You wouldn't want anyone to be able to decrypt anyone else's encrypted message would you?
It is impossible to tell from your code fragment where your private key is stored, or what the contents (or even class) of keyRefs is.
EDIT: In response to above OP's comment. And clarification.
*"The public key itself is the public part of a RSA-key-pair stored on the server. The encrypted message was created on the server by first JSON-encoding the object, then encrypted with the private-key with OPENSSL_PKCS1_PADDING, then base64-encoded, and then JSON-encoded again as a part of the final message. The message and public key is stored on the client. What i want is to decrypt the message on the client by using the public key. As i said, im not very good at this subject so i might have tried to do this the wrong way"*
Thats not how public key cryptography works. The server and client exchange public keys. Then each of them use the other's public key to encrypt data sent to the opposite party. The receiving party always uses their own private key to decrypt the message.
If you want the server to generate an encrypted response, have the client pass their public key in the request, use that public key to encrypt the response, and then decrypt the response on the client with the client's private key.