Has anyone ever got WS-Trust to work in JBoss 7? - jboss7.x

I've literally tried everything under the sun to get token based WS-Trust Web Services to work, to no avail. I can obtain a token from an STS, but the life of me, I can not figure out how make the WS server secure and accessible from the outside using a token.
So what I would love to know, is if anyone has ever got this to work on JBoss 7. I'm not interested in "this and that on jboss should give you some information". Been there done that - doesn't work. Have YOU been able to get it to work?

I looked at picketlink to secure web services using SAML but it appears to be exposing the SAML authentication using a JAAS security context. So instead I just wrote a custom handler using the picketlink API to secure the WS. The handler essentially does the same thing (i.e. saml assertion expiration and digital signature validation check) as the SAMLTokenCertValidatingCommonLoginModule available in picketlink jars but passes the SAML attributes into WS message context instead of passing it along as a JAAS security context.
Find below the code snippet.
See org.picketlink.identity.federation.bindings.jboss.auth.SAMLTokenCertValidatingCommonLoginModule
class of the picketlink-jbas-common source for implementation of methods getX509Certificate, validateCertPath used in the custom handler.
public class CustomSAML2Handler<C extends LogicalMessageContext> implements SOAPHandler {
protected boolean handleInbound(MessageContext msgContext) {
logger.info("Handling Inbound Message");
String assertionNS = JBossSAMLURIConstants.ASSERTION_NSURI.get();
SOAPMessageContext ctx = (SOAPMessageContext) msgContext;
SOAPMessage soapMessage = ctx.getMessage();
if (soapMessage == null)
throw logger.nullValueError("SOAP Message");
// retrieve the assertion
Document document = soapMessage.getSOAPPart();
Element soapHeader = Util.findOrCreateSoapHeader(document.getDocumentElement());
Element assertion = Util.findElement(soapHeader, new QName(assertionNS, "Assertion"));
if (assertion != null) {
AssertionType assertionType = null;
try {
assertionType = SAMLUtil.fromElement(assertion);
if (AssertionUtil.hasExpired(assertionType))
throw new RuntimeException(logger.samlAssertionExpiredError());
} catch (Exception e) {
logger.samlAssertionPasingFailed(e);
}
SamlCredential credential = new SamlCredential(assertion);
if (logger.isTraceEnabled()) {
logger.trace("Assertion included in SOAP payload: " + credential.getAssertionAsString());
}
try {
validateSAMLCredential(credential, assertionType);
ctx.put("roles",AssertionUtil.getRoles(assertionType, null));
ctx.setScope("roles", MessageContext.Scope.APPLICATION);
} catch (Exception e) {
logger.error("Error: " + e);
throw new RuntimeException(e);
}
} else {
logger.trace("We did not find any assertion");
}
return true;
}
private void validateSAMLCredential(SamlCredential credential, AssertionType assertion) throws LoginException, ConfigurationException, CertificateExpiredException, CertificateNotYetValidException {
// initialize xmlsec
org.apache.xml.security.Init.init();
X509Certificate cert = getX509Certificate(credential);
// public certificate validation
validateCertPath(cert);
// check time validity of the certificate
cert.checkValidity();
boolean sigValid = false;
try {
sigValid = AssertionUtil.isSignatureValid(credential.getAssertionAsElement(), cert.getPublicKey());
} catch (ProcessingException e) {
logger.processingError(e);
}
if (!sigValid) {
throw logger.authSAMLInvalidSignatureError();
}
if (AssertionUtil.hasExpired(assertion)) {
throw logger.authSAMLAssertionExpiredError();
}
}
}

Related

FCM Authorization always fails

Today i wanted to switch from GCM to FCM so i set up everything needed and wanted to implement the server side code. I used the gcm4j library and changed it so that the adress goes to https://fcm.googleapis.com/fcm/send.
So im doing the following:
FCM fcm = new FCMDefault(new FCMConfig().withKey(FCMGlobals.FCM_API_KEY));
FCMRequest request = new FCMRequest().withRegistrationId(android.getRegistration())
// .withCollapseKey(collapseKey)
.withDelayWhileIdle(true)
.withDataItem(FCMGlobals.FCM_PARAM_CODE, code)
.withDataItem(FCMGlobals.FCM_PARAM_USER_ID, "" + user.getId())
.withDataItem(FCMGlobals.FCM_PARAM_ADDITION, "" + addition);
ListenableFuture<FCMResponse> responseFuture = fcm.send(request);
Futures.addCallback(responseFuture, new FutureCallback<FCMResponse>() {
public void onFailure(Throwable t) {
log.error(t);
}
#Override
public void onSuccess(FCMResponse response) {
log.info(response.toString());
}
});
The implementation for that is:
protected FCMResponse executeRequest(FCMRequest request) throws IOException {
byte[] content = this.objectMapper.writeValueAsBytes(request);
HttpURLConnection conn = this.connectionFactory.open(this.fcmUrl);
conn.setRequestMethod("POST");
conn.addRequestProperty("Authorization", getAuthorization(request));
conn.addRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
conn.setFixedLengthStreamingMode(content.length);
LoggerFactory.getLogger("FCMDefaultAbstract").info("Authorization: " + conn.getRequestProperty("Authorization"));
LoggerFactory.getLogger("FCMDefaultAbstract").info("Content-Type: " + conn.getRequestProperty("Content-Type"));
LoggerFactory.getLogger("FCMDefaultAbstract").info("send: " + new String(content));
try (OutputStream outputStream = conn.getOutputStream()) {
IOUtils.write(content, outputStream);
} catch (Exception e) {
throw new FCMNetworkException("Error sending HTTP request to FCM", e);
}
FCMResponse response;
try (InputStream inputStream = conn.getInputStream()) {
response = this.objectMapper.readValue(IOUtils.toByteArray(inputStream), FCMResponse.class);
} catch (IOException e) {
try (InputStream inputStreamError = conn.getErrorStream()) {
String str = inputStreamError != null ? IOUtils.toString(inputStreamError) : "No error details provided";
int responseCode = conn.getResponseCode();
if (responseCode < 500) {
throw new FCMNetworkException(conn.getResponseCode(), str.trim(), e);
} else {
throw new FCMNetworkException(conn.getResponseCode(), str.trim(), checkForRetryInResponse(conn), e);
}
}
}
response.setRequest(request);
response.setRetryAfter(checkForRetryInResponse(conn));
Iterator<String> iteratorId = request.getRegistrationIds().iterator();
Iterator<FCMResult> iteratorResponse = response.getResults().iterator();
while (iteratorId.hasNext() && iteratorResponse.hasNext()) {
iteratorResponse.next().setRequestedRegistrationId(iteratorId.next());
}
if (iteratorId.hasNext()) {
LOG.warn("Protocol error: Less results than requested registation IDs");
}
if (iteratorResponse.hasNext()) {
LOG.warn("Protocol error: More results than requested registation IDs");
}
return response;
}
Here the log output:
FCMDefaultAbstract Authorization: null
FCMDefaultAbstract Content-Type:application/json
FCMDefaultAbstract send: {"registration_ids":["dMpvzp*************************************2lRsSl_5lFET2"],"data":{"CODE":"201","USER_ID":"1","ADDITION":"1468083549493"},"delay_while_idle":true}
FCM FCMNetworkException: HTTP 401: No error details provided
The Authorization header is not null in fact. it is correctly set with my FCM API Key. Only the HTTPUrlConnection implementation says to return null if someone trys to access Authorization key.
As you can see i am not able to connect with FCM. The Code 401 means that authentication failed.
What could be the problem here?
Check that you are using a server type API-KEY, and not a client or browser API-KEY.
If you are using Firebase you can find the API-KEY in
Project Settings > Cloud Messaging
If you are using cloud console, or you are not sure which key you are using,
you can generate a new key through through https://console.cloud.google.com
Quoting the documentation
https://firebase.google.com/docs/cloud-messaging/concept-options#credentials
Server key: A server key that authorizes your app server for access to
Google services, including sending messages via Firebase Cloud
Messaging. [...]
Important: Do not include the server key anywhere in your client code.
Also, make sure to use only server keys to authorize your app server.
Android, iOS, and browser keys are rejected by FCM.

Basic Auth to Receive Token in Spring Security

I am implementing a RESTful API where the user must authenticate. I want the user to POST their credentials in order to receive a JSON web token (JWT), which is then used for the remainder of the session. I have not found any good sources of information to set this up. In particular, I'm having trouble with the filter. Does anybody have any information or tutorials to help me set this up?
The people at Stormpath have quite a straightforward solution for achieving Oauth. Please take a look at Using Stormpath for API Authentication.
As a summary, your solution will look like this:
You will use the Stormpath Java SDK to easily delegate all your user-management needs.
When the user presses the login button, your front end will send the credentials securely to your backend-end through its REST API.
By the way, you can also completely delegate the login/register/logout functionality to the Servlet Plugin. Stormpath also supports Google, Facebook, LinkedIn and Github login.
Your backend will then try to authenticate the user against the Stormpath Backend and will return an access token as a result:
/**
* Authenticates via username (or email) and password and returns a new access token using the Account's ApiKey
*/
public String getAccessToken(String usernameOrEmail, String password) {
ApiKey apiKey = null;
try {
AuthenticationRequest request = new UsernamePasswordRequest(usernameOrEmail, password);
AuthenticationResult result = application.authenticateAccount(request);
Account account = result.getAccount();
ApiKeyList apiKeys = account.getApiKeys();
for (ApiKey ak : apiKeys) {
apiKey = ak;
break;
}
if (apiKey == null) {
//this account does not yet have an apiKey
apiKey = account.createApiKey();
}
} catch (ResourceException exception) {
System.out.println("Authentication Error: " + exception.getMessage());
throw exception;
}
return getAccessToken(apiKey);
}
private String getAccessToken(ApiKey apiKey) {
HttpRequest request = createOauthAuthenticationRequest(apiKey);
AccessTokenResult accessTokenResult = (AccessTokenResult) application.authenticateApiRequest(request);
return accessTokenResult.getTokenResponse().getAccessToken();
}
private HttpRequest createOauthAuthenticationRequest(ApiKey apiKey) {
try {
String credentials = apiKey.getId() + ":" + apiKey.getSecret();
Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
headers.put("Accept", new String[]{"application/json"});
headers.put("Content-Type", new String[]{"application/x-www-form-urlencoded"});
headers.put("Authorization", new String[]{"Basic " + Base64.encodeBase64String(credentials.getBytes("UTF-8"))});
Map<String, String[]> parameters = new LinkedHashMap<String, String[]>();
parameters.put("grant_type", new String[]{"client_credentials"});
HttpRequest request = HttpRequests.method(HttpMethod.POST)
.headers(headers)
.parameters(parameters)
.build();
return request;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Then, for every authenticated request, your backend will do:
/** This is your protected API */
public void sayHello(String accessToken) throws OauthAuthenticationException {
try {
if (verify(accessToken)) {
doStartEngines(); //Here you will actually call your internal doStartEngines() operation
}
} catch (OauthAuthenticationException e) {
System.out.print("[Server-side] Engines not started. accessToken could not be verified: " + e.getMessage());
throw e;
}
}
private boolean verify(String accessToken) throws OauthAuthenticationException {
HttpRequest request = createRequestForOauth2AuthenticatedOperation(accessToken);
OauthAuthenticationResult result = application.authenticateOauthRequest(request).execute();
System.out.println(result.getAccount().getEmail() + " was successfully verified");
return true;
}
private HttpRequest createRequestForOauth2AuthenticatedOperation(String token) {
try {
Map<String, String[]> headers = new LinkedHashMap<String, String[]>();
headers.put("Accept", new String[]{"application/json"});
headers.put("Authorization", new String[]{"Bearer " + token});
HttpRequest request = HttpRequests.method(HttpMethod.GET)
.headers(headers)
.build();
return request;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
All this will not need any special Spring Security configuration, this is plain Java code that you can run in any framework.
Please take a look here for more information.
Hope that helps!
Disclaimer, I am an active Stormpath contributor.
Here's a working sample code from Spring Security OAuth github.
https://github.com/spring-projects/spring-security-oauth/tree/master/tests/annotation/jwt
You probably don't even need to mess with the filters as shown in the above example. If you've custom needs, please post some sample code.

WCF: How to handle errors when calling another WCF service?

I have a project where I should write WCF service that calls another WCF service. It looks as following:
[ServiceContract]
public interface IAsurService
{
[OperationContract(ReplyAction = AsurService.ReplyAction_GetCatalogList)]
Message GetCatalogList();
public Message GetCatalogList()
{
// The external client service
GetNsiClient client = new GetNsiClient();
authContext auth = new authContext
{
company = "asur_nsi",
password = "lapshovva",
user = "dogm_LapshovVA"
};
catalogs catalogs = client.getCatalogList(auth);
How can I handle errors in this case? Can I use standard fault contract approach like this:
[DataContract]
public class AsurDataFaultException
{
private string reason;
[DataMember]
public string Reason
{
get { return reason; }
set { reason = value; }
}
}
public Message GetCatalogList()
{
// The external client service
GetNsiClient client = new GetNsiClient();
authContext auth = new authContext
{
company = "asur_nsi",
password = "lapshovva",
user = "dogm_LapshovVA"
};
catalogs catalogs = null;
try
{
catalogs = client.getCatalogList(auth);
}
catch (Exception exception)
{
AsurDataFaultException fault = new AsurDataFaultException();
fault.Reason = "The error: " + exception.Message.ToString();
throw new FaultException<AsurDataFaultException>(fault);
}
Or something else?
Thank you in advance.
Goran
If the remote WCF service's failure causes a failure of your code, then obviously you should throw a fault but that fault should not expose the inner details of the remote WCF service or its exception, like so:
catch (Exception exception)
{
// Indicate all that you may expose -- that the data is unavailable.
DataUnavailableFault fault = new DataUnavailableFault();
throw new FaultException<DataUnavailableFault>(fault);
}
You may of course also use a retry mechanism, but obviously if what you end up with is a failure in the remote service, you should indicate that to your caller by throwing an appropriate fault.

handling app that requires web service - dealing with EndpointNotFoundExceptions

I'm almost finished my first WP7 application and I'd like to publish it to the marketplace. However, one of the stipulations for a published app is that it must not crash unexpectedly during use.
My application almost completely relies on a WCF Azure Service - so I must be connected to the Internet at all times for my functions to work (communicating with a hosted database) - including login, adding/deleting/editing/searching clients and so forth.
When not connected to the internet, or when the connection drops during use, a call to the web service will cause the application to quit. How can I handle this? I figured the failure to connect to the service would be caught and I could handle the exception, but it doesn't work this way.
LoginCommand = new RelayCommand(() =>
{
ApplicationBarHelper.UpdateBindingOnFocussedControl();
MyTrainerReference.MyTrainerServiceClient service = new MyTrainerReference.MyTrainerServiceClient();
// get list of clients from web service
service.LoginCompleted += new EventHandler<LoginCompletedEventArgs>(service_LoginCompleted);
try
{
service.LoginAsync(Email, Password);
}
**catch (Exception ex)
{
throw new Exception(ex.Message);
}**
service.CloseAsync();
});
EDIT:
My main problem is how to handle the EndpointNotFoundException in WP7 without the application crashing.
Thanks,
Gerard.
Your code should look like
LoginCommand = new RelayCommand(Login);
...
public void Login()
{
var svc = new MyTrainerReference.MyTrainerServiceClient();
try
{
svc.LoginCompleted += LoginCompleted;
svc.LoginAsync();
}
catch (Exception e)
{
svc.CloseAsync();
ShowError(e);
}
}
private void LoginCompleted(object sender, LoginCompletedEventArgs e)
{
((MyTrainerReference.MyTrainerServiceClient)sender).LoginCompleted -= LoginCompleted;
((MyTrainerReference.MyTrainerServiceClient)sender).CloseAsync();
if (e.Error == null && !e.Cancelled)
{
// TODO process e.Result
}
else if (!e.Cancelled)
{
ShowError(e.Error);
}
}
private void ShowError(Exception e)
{
// TODO show error
MessageBox.Show(e.Message, "An error occured", MessageBoxButton.OK);
}
Your code calls LoginAsync and then immediately CloseAsync, I think this will cause problems...

Possible to access remote EJBs from a custom LoginModule?

I found some nice hints on how to write a custom realm and loginModule. I'm wondering though if it is possible to access a remote EJB within the custom loginModule.
In my case, I have remote EJBs that provide access to user-entities (via JPA) -- can I use them (e.g. via #EJB annotation)?
Ok, I found the answer myself: works fine! I can get a reference to the remote SLSB via an InitialContext.
Here's the code:
public class UserLoginModule extends AppservPasswordLoginModule {
Logger log = Logger.getLogger(this.getClass().getName());
private UserFacadeLocal userFacade;
public UserLoginModule() {
try {
InitialContext ic = new InitialContext();
userFacade = (UserFacadeLocal) ic.lookup("java:global/MyAppServer/UserFacade!com.skalio.myapp.beans.UserFacadeLocal");
log.info("userFacade bean received");
} catch (NamingException ex) {
log.warning("Unable to get userFacade Bean!");
}
}
#Override
protected void authenticateUser() throws LoginException {
log.fine("Attempting to authenticate user '"+ _username +"', '"+ _password +"'");
User user;
// get the realm
UserRealm userRealm = (UserRealm) _currentRealm;
try {
user = userFacade.authenticate(_username, _password.trim());
userFacade.detach(user);
} catch (UnauthorizedException e) {
log.warning("Authentication failed: "+ e.getMessage());
throw new LoginException("UserLogin authentication failed!");
} catch (Exception e) {
throw new LoginException("UserLogin failed: "+ e.getMessage());
}
log.fine("Authentication successful for "+ user);
// get the groups the user is a member of
String[] grpList = userRealm.authorize(user);
if (grpList == null) {
throw new LoginException("User is not member of any groups");
}
// Add the logged in user to the subject's principals.
// This works, but unfortunately, I can't reach the user object
// afterwards again.
Set principals = _subject.getPrincipals();
principals.add(new UserPrincipalImpl(user));
this.commitUserAuthentication(grpList);
}
}
The trick is to separate the interfaces for the beans from the WAR. I bundle all interfaces and common entities in a separate OSGi module and deploy it with asadmin --type osgi. As a result, the custom UserLoginModule can classload them.