JWT Authentication fails in Hyperledger Composer - hyperledger-composer

I've been following Caroline's blog to setup a multi-user composer rest server. So, I have two servers viz. Admin Server and the User Server.
As mentioned in the tutorial I:
Started the Admin Server with no authentication and single user mode. I started this server with Admin's card.
Started the User Server with passport JWT authentication in multi-user mode. I started this server with Admin's card as well.
Created a User participant and generated a card for user from the admin server.
In this step I'm trying to exchange the JWT Token with the User Server(#2) and I'm able to get the token as well.
Ping user server with JWT Token. This results in "Error: Authorization Required".
I've followed Chris Ganga's blog for implementing JWT. My COMPOSER_PROVIDERS is:
COMPOSER_PROVIDERS='{
"jwt": {
"provider": "jwt",
"module": "/home/composer/node_modules/custom-jwt.js",
"secretOrKey": "somesecretkey",
"authScheme": "saml",
"successRedirect": "/",
"failureRedirect":"/"
}
}'
I'm exchanging JWT token for the first time from a Java service. To create a bearer token, I've written the following code:
public static String getBearerToken(String username, String id) throws UnsupportedEncodingException {
return Jwts.builder()
.claim("timestamp", System.currentTimeMillis())
.claim("username", username)
.claim("id", id)
.signWith(
SignatureAlgorithm.HS256,
"somesecretkey".getBytes("UTF-8")
).compact();
}
With this, I'm able to get the token. Next, I use this token to import the card into the wallet on User server:
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set("X-Access-Token",getAccess_token(participantEmail));
headers.set("x-api-key", userServerKey);
LinkedMultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
params.add("card", new FileSystemResource(card));
params.add("name", participantEmail);
HttpEntity<LinkedMultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(CARD_IMPORT_URL);
ResponseEntity<String> responseEntity = restTemplate.exchange(builder.build().encode().toUri(),
HttpMethod.POST, requestEntity, String.class);
However, this results in:
Unhandled error for request POST /api/Wallet/import: Error: Authorization Required
Finding 1
Generally, when we exchange the JWT for the first time using auth bearer, a db named "test" is created in mongo. This db contains three collections: accessToken, user and userIdentity. However, in my case when I exchange the token, no db is created in mongo. Any suggestions on how I can debug this?
Note:
This whole setup was working perfectly fine until I decided to prune and restart the network from scratch.

The issue here was I was using the same name for mongodb container across all the hosts. And since all the containers connect to the swarm network, there were 8 mongodb containers with the same name which is quite a silly mistake. It caused an issue when the docker container of composer-rest-server tried to connect to the mongodb container. This connection is defined in COMPOSER_DATASOURCES variable. Using the different name for each of the mongo containers across all the hosts solved this issue.

Related

Spring Context is not updated SecurityContextHolder.getContext().setAuthentication()

We have Spring Framework 4.1.0.RELEASE project. Our project is ingrated with third-party Service. Authentication is implemented through OAuth 2.0.
Our clients create some entity within our site and the entity is pushed to the third-party Service through Http request. Access tokens are expired periodically. Each request to the third-party Service
goes through checkAuthorization() method, which checks 401 Response, exchange a new token and replace old/new tokens. The access tokens is stored in
public HttpResponse checkAuthorization(HttpUriRequest httpUriRequest, String accessToken) {
HttpResponse response = tokenExchange(
clientId,
clientSecret,
refreshToken,
redirectUri
);
Json json = getJson(response);
String newAcceessToken = json.get("access_token");
ContextUser context = (ContextUser)oldAuth.getPrincipal();
context.setAccessesToken(newAcceessToken);
Authentication newAuth = new UsernamePasswordAuthenticationToken(
context, oldAuth.getCredentials(), oldAuth.getAuthorities()
);
SecurityContextHolder.getContext().setAuthentication(newAuth);
}
That process is working in the production site. But from time to time i encounter with that situation:
Several requests to the third-party Service has done with 401 Response within one minute. Differences are in milliseconds only. Corresponding request bodies are different. Requests are not the same. Each request in checkAuthorization() receives the same invalid token and each request update this token in checkAuthorization(). Despite of tokenExchange() was done with 200 Ok and the new token is set into ContextUser the next third-party request in checkAuthorization() receive the old token.
ContextUser context = (ContextUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
...
checkAuthorization(httpObject, context)
It is happen from time to time and in most cases it is working successfully. Third-party Service responded that no problem with their Service.
What is cause of this strange behaviour of updating access tokens?

How to do the token exchange impersonation in Keycloak?

I’m trying to impersonate a user using the token exchange functionality but I always get the error that the client is not allowed to exchange.
This is the request I’m doing using the WebFlux WebClient:
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange");
formData.add("requested_token_type", "urn:ietf:params:oauth:token-type:access_token");
formData.add("client_id", keycloakClientId);
formData.add("requested_subject", userId);
formData.add("subject_token", token);
return WebClient.create()
.post()
.uri(authServerUrl + "/realms/" + authServerRealm + "/protocol/openid-connect/token")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.body(BodyInserters.fromFormData(formData))
.exchangeToMono(clientResponse -> clientResponse.bodyToMono(AccessTokenResponse.class));
The response I get is this one:
{"error":"access_denied","error_description":"Client not allowed to exchange"}
How do I get the token above to be exchanged:
KeycloakBuilder.builder()
.serverUrl(authServerUrl)
.realm(authServerRealm)
.grantType(OAuth2Constants.PASSWORD)
.username(keycloakAdminUsername)
.password(keycloakAdminPassword)
.clientId(keycloakClientId)
.build();
What I am doing wrong? Why does the client need to be allowed to exchange since this impersonation and not client token exchange. Does the client need to be confidential for this to be done?
In addition to Chris' answer, I also realized that I had to do an additional step: Client -> Service account roles → Client roles → in the dropdown select ‘Realm management’ and assign Impersonation role. I tried this after I installed Keycloak with fine grained permissions as Chris indicated, and following the Keycloak documentation (which sucks btw) https://www.keycloak.org/docs/15.0/securing_apps/index.html#_token-exchange
You need to grant permission (on the target client) to allow keycloakClientId to mint a token for the target client.
7.1.1 in the docs:
https://www.keycloak.org/docs/latest/securing_apps/#internal-token-to-internal-token-exchange
You will also need to start keycloak with the following flags, so that you enable fine grained permissions:
-Dkeycloak.profile.feature.admin_fine_grained_authz=enabled -Dkeycloak.profile.feature.token_exchange=enabled

User creation with keycloak

I am trying to create user with keycloak's /users endpoint in a spring boot project .
These are the steps I have followed
First created an admin in master realm and admin-cli client.
Used that to get instance of keycloak for further operations.
return Keycloak.getInstance(
keycloakProperties.getAuthServerUrl(),
"master",
adminConfig.getUserName(),
adminConfig.getPassword(),
"admin-cli");
I am able to get the user created if I don't add the client representation in user.
If I add the CredentialRepresentation inside the userRepresentation object ,I am getting the BadRequest error (400 code) without any details . I looked up the keycloak server logs that too wasnt helpful.
As a try,I did the user creation first and reset password later,then also user created,but unable to set password with reset-password endpoint (same error) .As mentioned in the documents,I have set the enabled property of userRepresentation to true.
Keycloak version : 12.0.4 ,Spring boot version : 2.1.6.RELEASE
I tried to read the entity from the response received, but failed in that too.
Any help to figure out the issue will e appreciated.
// Code for usercreation
UserRepresentation userRepresentation = new UserRepresentation();
userRepresentation.setUsername(userModel.getEmail());
userRepresentation.setEmail(userModel.getEmail());
userRepresentation.setCredentials(Arrays.asList(createPasswordCredentials(userModel.getPassword())));
userRepresentation.setEnabled(true);
Keycloak keycloak = getKeycloakInstance(keycloakProperties);
Response response = keycloak.realm(keycloakProperties.getRealm()).users().create(userRepresentation);
log.info("Response Code {}", response.getStatus());
Code for CredentialRepresentation :
private static CredentialRepresentation createPasswordCredentials(String password) {
CredentialRepresentation passwordCredentials = new CredentialRepresentation();
passwordCredentials.setTemporary(false);
passwordCredentials.setType(CredentialRepresentation.PASSWORD);
passwordCredentials.setValue(password);
return passwordCredentials;
}
I just want to update the real cause behind this issue,The server I used was of old version ,and the issue got fixed when tested against latest keycloak server.
Thank you for the support guys :)

send sms using third-party REST service inside a Spring boot application

i want to send sms to user mobile number using third-party REST service inside a Spring boot application.
API like follow:
http://cloud.smsindiahub.in/vendorsms/pushsms.aspx?user=abc&password=xyz&msisdn=919898xxxxxx&sid=SenderId&msg=test%20message&fl=0&gwid=2
user: Your login username.
password: Your login password.
msisdn: Single mobile number
sid: Approved sender id(Only 6 characters).
msg: Your message content(Minimum 459 characters/3 messages). Note: If you are using template then edit only the dynamic part which is in ##Field##.
fl: if flash message then 1 or else 0
gwid: 2 (its for Transactions route.)
Note: Only 100 mobile numbers are allowed.
So i want to ask can i do using RestTemplate or any other way better in spring boot?
also i want to save response which like below which is josn response:
{"ErrorCode":"000","ErrorMessage":"Success","JobId":"381a8090-b230-42fa-ac04-157cc2142bfa","MessageData":[{"MobileNumber":"919898xxxxxx ","MessageParts":[{"MessageId": "919898xxxxxx -67e3765cdf034f438a432eacb88d0c14","MessagePartId":1,"MessageText":"test message"}]}]}
and for REST API username and password saving application.xml is secure?also how to save and access in class.
if any example for the same just send me link.
thanks.
First, Your API provider should a different authentication method like Auth key as using username and password in RestAPI is not good option.
Now, You can call 3rd party RestAPI url in springboot as below:
private static void custom_func()
{
final String uri = "http://cloud.smsindiahub.in/vendorsms/pushsms.aspx?user=abc&password=xyz&msisdn=919898xxxxxx&sid=SenderId&msg=test%20message&fl=0&gwid=2";
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject(uri, String.class);
System.out.println(result);
}

Spring OAuth2: support auth and resource access with both SSO and custom auth server

I've found similar issue but it's unanswered, so I suppose I'm going to duplicate question a little.
I am using Spring OAuth2 to implement separate resource and custom authentification servers.
I've already configured interaction with auth server through issuing&validating JWT tokens and everything seems fine.
Now I'm trying to add SSO functionality but really stuck with it. I've researched the official Spring examples and attached guide but it is very short worded when it comes to connecting SSO part with custom server authentication. And actually author uses only external provider resource ('user' info) to show process.
I think it is normal thing to have all this SSO means of authentication and also custom registration. I can see it works well with stackoverflow for example.
I am loking for directions where to find any info about handling on resource server different kind of tokens issued by multiply SSO providers and also from custom auth server.
Maybe I can use auth chain to do this and some mean to distinguish token format to know how to process it. Is it possible with Spring OAuth2? Or I need to do this magic somehow manually?
For now I have just one 'maybe strange' idea:
To not involve my own resource server with this SSO stuff at all. After receiving Facebook (for example) token - just exchange it for api JWT token with custom auth server (associating or creating user on the way) and then work with resource server on standard basics
EDITED:
I've found at least something. I've read about configuring filters in authorization chain and translate given social tokens to my custom JWT-s as 'post authenticate'(not a crazy idea after all). But it mostly done with SpringSocial.
So now question is: how to do that?
Forgot to say that I am using Password Grant for authentication on custom server. Clients will be only trusted application and I do not even sure about browser client (thinking about only native mobile options). Even if I decide to have browser client I'll make sure it's going to have backend to store sencetive information
Ok, so after struggling to implement such behavior I've stuck with two different libraries (Spring Social & OAuth2). I decided to go my own way and do it with just Spring OAuth2:
I have the resource server, authentication server and client(backed up by Java and uses OAuth2 Client library, but it can be any other client) - my resources can be consumed only with my own JWT auth token given by my own auth server
in a case of a custom registration: client obtains JWT token(with refresh token) from auth server and sends it to the res server. Res server validates it with public key and gives the resource back
in a case of SSO: client obtains Facebook(or other social platform token) and exchanges it for my custom JWT token with my custom auth server. I've implemented this on my auth server using custom SocialTokenGranter(currently handles facebook social token only. For every social network I'll need separate grant type). This class makes an additional call to facebook auth server to validate token and obtain user info. Then it retrieves the social user from my db or creates new and returns JWT token back to the client. No user merging is done by now. it is out of scope for now.
public class SocialTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "facebook_social";
GiraffeUserDetailsService giraffeUserDetailsService; // custom UserDetails service
SocialTokenGranter(
GiraffeUserDetailsService giraffeUserDetailsService,
AuthorizationServerTokenServices tokenServices,
OAuth2RequestFactory defaultOauth2RequestFactory,
ClientDetailsService clientDetailsService) {
super(tokenServices, clientDetailsService, defaultOauth2RequestFactory, GRANT_TYPE);
this.giraffeUserDetailsService = giraffeUserDetailsService;
}
#Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails clientDetails, TokenRequest request) {
// retrieve social token sent by the client
Map<String, String> parameters = request.getRequestParameters();
String socialToken = parameters.get("social_token");
//validate social token and receive user information from external authentication server
String url = "https://graph.facebook.com/me?access_token=" + socialToken;
Authentication userAuth = null;
try {
ResponseEntity<FacebookUserInformation> response = new RestTemplate().getForEntity(url, FacebookUserInformation.class);
if (response.getStatusCode().is4xxClientError()) throw new GiraffeException.InvalidOrExpiredSocialToken();
FacebookUserInformation userInformation = response.getBody();
GiraffeUserDetails giraffeSocialUserDetails = giraffeUserDetailsService.loadOrCreateSocialUser(userInformation.getId(), userInformation.getEmail(), User.SocialProvider.FACEBOOK);
userAuth = new UsernamePasswordAuthenticationToken(giraffeSocialUserDetails, "N/A", giraffeSocialUserDetails.getAuthorities());
} catch (GiraffeException.InvalidOrExpiredSocialToken | GiraffeException.UnableToValidateSocialUserInformation e) {
// log the stacktrace
}
return new OAuth2Authentication(request.createOAuth2Request(clientDetails), userAuth);
}
private static class FacebookUserInformation {
private String id;
private String email;
// getters, setters, constructor
}
}
And from class extending AuthorizationServerConfigurerAdapter:
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
List<TokenGranter> granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
granters.add(new SocialTokenGranter(giraffeUserDetailsService, endpoints.getTokenServices(), endpoints.getOAuth2RequestFactory(), endpoints.getClientDetailsService()));
return new CompositeTokenGranter(granters);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
...
.allowFormAuthenticationForClients() // to allow sending parameters as form fields
...
}
Every JWT token request is going to 'host:port + /oauth/token' url
Depending on 'Grant type' the server will handle such requests differently. Currently I have 'password'(default), 'refresh_token' and 'facebook_social'(custom) grant types
For default 'password' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
username
password
scope (if not explicitly set in auth server configuration for current client)
grantType
For 'refresh_token' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
refresh_token
grantType
For 'facebook_social' Grant type the client should send next parameters:
clientId
facebook_social_token (custom field)
grantType
Based on the client design the way to send these requests will be different.
In my case with test Java based client which uses Spring OAuth2 library to obtain the social token I do the token exchange procedure with the redirect in controller(controller being invoked using url defined in facebook dev page configuration).
It can be handled in two stages: after obtaining facebook social token JavaScript can make a separate explicit call to my auth server to exchange tokens.
You can see Java client implementation examples here, but I doubt that you're going to use Java client in production:https://spring.io/guides/tutorials/spring-boot-oauth2/

Resources