Basic Auth to Receive Token in Spring Security - api

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.

Related

Azure function app using CSOM and Azure AD for authentication

I'm not sure if this is possible but my objective is an Azure function app that can use the SharePoint CSOM. I'm stuck on how to do the authorization with no user credentials. I've pieced together the code below but it throws a 401 unauthorized. This could be a configuration issue which I've had problems with when doing a JavaScript application and a Web Api. But I'm also wondering if this is even feasible or if I'm going about it the wrong way. Some key points before the code:
My front end demo app was created with a secret to be used in authentication
My Api demo app was granted API permissions to Azure AD, Microsoft Graph, and SharePoint
My Api demo app exposed an API and the front end demo app was added as an authorized client app
private static async Task ProcessMessageAsync(string myQueueItem, ILogger log)
{
const string mName = "ProcessMessageAsync()";
string resource = "https://my-Portal.onmicrosoft.com/demoapp-api"; //demoapp-api with permission to sharepoint
string clientId = "guid-of-demoapp-frontend"; //demoapp-frontend
string clientSecret = "secret-from-demoapp-frontend"; //demoapp-frontend secret
string siteUrl = "https://my-portal.sharepoint.com/sites/my-site"; //sharepoint site
string authorityUri = "https://login.microsoftonline.com/my-portal.onmicrosoft.com";
try
{
using (ClientContext ctx = await GetClientContext(authorityUri, siteUrl, resource, clientId, clientSecret))
{
Web web = ctx.Web;
ctx.Load(web);
ctx.ExecuteQuery();
log.LogInformation($"found site : {web.Title}");
}
}
catch (Exception ex) {
Console.WriteLine("***Unexpected Exception in {0} *** : {1}", mName, ex.Message);
log.LogInformation("***Unexpected Exception in {0} *** : {1}", mName, ex.Message);
while (ex.InnerException != null)
{
ex = ex.InnerException;
Console.WriteLine(ex.Message);
}
}
}
public async static Task<ClientContext> GetClientContext(string authorityuri, string siteUrl, string resource, string clientId, string clientSecret)
{
AuthenticationContext authContext = new AuthenticationContext(authorityuri);
AuthenticationResult ar = await GetAccessToken(authorityuri, resource, clientId, clientSecret);
string token = ar.AccessToken;
var ctx = new ClientContext(siteUrl);
ctx.ExecutingWebRequest += (s, e) =>
{
e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + ar.AccessToken;
};
return ctx;
}
static async Task<AuthenticationResult> GetAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = await context.AcquireTokenAsync(
resource, // the resource (app) we are going to access with the token
clientCredential); // the client credentials
return authenticationResult;
}
Update
After further research I think the problem might be that I was using delegate permissions and instead I need application permissions (which require that I have admin access in the directory).
You get token with following code:
AuthenticationResult ar = await GetAccessToken(authorityuri, resource, clientId, clientSecret);
string token = ar.AccessToken;
However, the token you get can only be used to access the resource you specified as the parameter. In your case, it is "https://my-Portal.onmicrosoft.com/demoapp-api".
So, you can only use that token to access your endpoint web API, not sharepoint directly.

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.

How to setup auth token security for WebAPI requests?

In following this tutorial (modifying it to use an application-based auth string rather than their user model), have the following TokenValidationAttribute defined and set this attribute on WebAPI controllers in order to verify that the API request came within my web application:
public class TokenValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
string token;
try
{
token = actionContext.Request.Headers.GetValues("Authorization-Token").First();
}
catch (Exception)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest)
{
Content = new StringContent("Missing Authorization-Token")
};
return;
}
try
{
var crypto = new SimpleCrypto.PBKDF2(); // type of encryption
var authPart = ConfigurationManager.AppSettings["AuthorizationTokenPart"];
var authSalt = GlobalVariables.AuthorizationSalt;
var authToken = GlobalVariables.AuthorizationToken;
if (authToken == crypto.Compute(authPart, authSalt))
{
// valid auth token
}
else
{
// invalid auth token
}
//AuthorizedUserRepository.GetUsers().First(x => x.Name == RSAClass.Decrypt(token));
base.OnActionExecuting(actionContext);
}
catch (Exception ex)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden)
{
Content = new StringContent("Unauthorized User")
};
return;
}
}
}
In my login class, I have the following method defined that returns a User object if valid:
private User IsValid(string username, string password)
{
var crypto = new SimpleCrypto.PBKDF2(); // type of encryption
using (var db = new DAL.DbContext())
{
var user = db.Users
.Include("MembershipType")
.FirstOrDefault(u => u.UserName == username);
if (user != null && user.Password == crypto.Compute(password, user.PasswordSalt))
{
return user;
}
}
return null;
}
As you can see, the user login validation method doesn't make a WebAPI call that would be to ~/api/User (that part works).
1) How do I generate a request with with auth token (only site-generated API requests are valid)? These could be direct API calls from code-behind, or JavaScript-based (AngularJS) requests to hydrate some objects.
2) I'm not entirely clear on what base.OnActionExecuting(actionContext); . What do I do if the token is valid/invalid?
i think the best practices to send authorization header is by added it on request header
request.Headers.Add("Authorization-Token",bla bla bla);
you can create webrequest or httprequest
maybe you should start from http://rest.elkstein.org/2008/02/using-rest-in-c-sharp.html
or http://msdn.microsoft.com/en-us/library/debx8sh9%28v=vs.110%29.aspx.
in my opinion in order to create proper login security and request you should apply a standard such as openid or oauth
cheers
I did something like this, LoginSession contains my token and is static (in my case its a shared service (not static))
public HttpClient GetClient()
{
var client = new HttpClient
{
Timeout = new TimeSpan(0, 0, 2, 0),
BaseAddress = new Uri(GetServiceAddress())
};
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
if (LoginSession.Token != null)
{
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", String.Format("Bearer {0}", LoginSession.Token.AccessToken));
}
return client;
}
notice this line:
client.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", String.Format("Bearer {0}", LoginSession.Token.AccessToken));

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

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();
}
}
}

Using Google experimental implementation of OAuth 2.0 to access existing API endpoints

According to this documentation, process of receiving OAuth access token is straightforward. I would like to see a list of all available API endpoints that is ready to accept OAuth 2.0 access token. But for my current needs i would like to somehow receive username and email of a user using OAuth 2.0 access token.
I successfully can receive, for example, data from this endpoint:
https://www.google.com/m8/feeds/contacts/default/full
But unable to receive data from this endpoint:
https://www.googleapis.com/userinfo/email
I tried both header-base and querystring-base approaches of passing single access token. Here is a header i tried:
Authorization: OAuth My_ACCESS_TOKEN
And I even tried OAuth 1.0 version of Authorization header, but... in OAuth 2.0 we do not have secret access token, for instance. Google use bearer tokens in his implementation of OAuth 2.0, so no additional credentials are required.
Anyone successfully received username and email using Google OAuth 2.0?
I found the answer I was looking for. I had to convert PHP to MVC, but pretty easy:
http://codecri.me/case/430/get-a-users-google-email-address-via-oauth2-in-php/
My MVC Login sandbox code looks like the following.
(using JSON.Net http://json.codeplex.com/)
public ActionResult Login()
{
string url = "https://accounts.google.com/o/oauth2/auth?";
url += "client_id=<google-clientid>";
url += "&redirect_uri=" +
// Development Server :P
HttpUtility.UrlEncode("http://localhost:61857/Account/OAuthVerify");
url += "&scope=";
url += HttpUtility.UrlEncode("http://www.google.com/calendar/feeds/ ");
url += HttpUtility.UrlEncode("http://www.google.com/m8/feeds/ ");
url += HttpUtility.UrlEncode("http://docs.google.com/feeds/ ");
url += HttpUtility.UrlEncode("https://mail.google.com/mail/feed/atom ");
url += HttpUtility.UrlEncode("https://www.googleapis.com/auth/userinfo.email ");
url += HttpUtility.UrlEncode("https://www.googleapis.com/auth/userinfo.profile ");
url += "&response_type=code";
return new RedirectResult(url);
}
The code returned is proof of Authorization token from the user, which then needs to be turn into a Authentication (accessToken) to access resources.
My MVC OAuthVerify then looks like:
public ActionResult AgentVerify(string code)
{
JObject json;
if (!string.IsNullOrWhiteSpace(code))
{
NameValueCollection postData = new NameValueCollection();
postData.Add("code", code);
postData.Add("client_id", "<google-clientid>");
postData.Add("client_secret", "<google-client-secret>");
postData.Add("redirect_uri", "http://localhost:61857/Account/OAuthVerify");
postData.Add("grant_type", "authorization_code");
try
{
json = JObject.Parse(
HttpClient.PostUrl(
new Uri("https://accounts.google.com/o/oauth2/token"), postData));
string accessToken = json["access_token"].ToString();
string refreshToken = json["refresh_token"].ToString();
bool isBearer =
string.Compare(json["token_type"].ToString(),
"Bearer",
true,
CultureInfo.CurrentCulture) == 0;
if (isBearer)
{
json = JObject.Parse(
HttpClient.GetUrl(
new Uri("https://www.googleapis.com/oauth2/v1/userinfo?alt=json"),
accessToken));
string userEmail = json["email"].ToString();
}
return View("LoginGood");
}
catch (Exception ex)
{
ErrorSignal.FromCurrentContext().Raise(ex); //ELMAH
}
}
return View("LoginBad");
}
To complete how everything works, I've included the HttpClient utility I created in case anyone needed it.
public class HttpClient
{
public static string GetUrl(Uri url, string OAuth)
{
string result = string.Empty;
using (WebClient httpClient = new WebClient())
{
httpClient.Headers.Add("Authorization","OAuth " + OAuth);
result = httpClient.DownloadString(url.AbsoluteUri);
}
return result;
}
public static string PostUrl(Uri url, NameValueCollection formData)
{
string result = string.Empty;
using (WebClient httpClient = new WebClient())
{
byte[] bytes = httpClient.UploadValues(url.AbsoluteUri, "POST", formData);
result = Encoding.UTF8.GetString(bytes);
}
return result;
}
}
Again, this is test code just to get it to function, I do not recommend using this as-is in a production environment.
try this:
curl -k https://www.googleapis.com/userinfo/email -H "Authorization: OAuth 1/g5_039aCIAfEBuL7OCyB31n1URYU5tUIDudiWKuxN1o"
output: email=name#gmail.com&isVerified=tru