Implement Authentication for servlet on publish instance CQ5/AEM - authentication

I have a scenario and any suggestions in implementing that will be of great help. I have a servlet created on publish that will have POST requests coming from a lot of other third party applications. This servlet just stores the incoming posted data in JCR. I have successfully created this servlet but now the requirement is to make this servlet secured so that only applications hitting this servlet with particular username and password should be entertained.
What can I do to achieve this?

The way I would go for it:
Ask those 3rd party applications to send you the username and password so you can validate them on your servlet, then decide if you will allow or reject the request.
from the servlet calling (the 3rd party application)
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
request.setAttribute("username", "a_valid_user");
request.setAttribute("password", "a_valid_password");
request.getRequestDispatcher("yourApp/YourServlet").forward(req, resp);
}
On your servlet:
String username = request.getParameter("username");
String password = request.getParameter("password");
if("a_valid_user".equals(username) && "a_valid_password".equals(password) {
// validate and go further
} else {
// do not process the request
}
The above example is valid just in case you can validate them on your side.
If this sample doesn't answer to your question, please provide more information about those 3rd party applications and the way you want to validate them.

You might consider using Google Client Library. I used it for authentication of users in an AEM publish instance. After the third party server is authenticated, you could use a separate AEM service account to handle POST processing.
Here' a SO post I made about integrating those libraries into AEM.
Google Client API in OSGI
With this you should be able set up authentication of the third party service account... as discussed here
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
I haven't actually done server to server auth in AEM, but it should be possible. But in a separate project (non AEM) I've used the Google Client Library for authenticating Service Accounts.

I recommend to use a two step process:
Step 1: Authentication and generate a token, you can use 3rd party service also to generate token.
Step 2: Call your servlet with this token, the servlet will validate token first and then use post data.

Thanks everyone for your replies. In the end I implemented the below code for authentication in cq :
final String authorization = request.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Basic")) {
StringTokenizer st = new StringTokenizer(authorization);
if (st.hasMoreTokens()) {
String basic = st.nextToken();
if (basic.equalsIgnoreCase("Basic")) {
String decodedStr = Base64.decode(st.nextToken());
LOGGER.info("Credentials: " + decodedStr);
int p = decodedStr.indexOf(":");
if (p != -1) {
String login = decodedStr.substring(0, p).trim();
String password = decodedStr.substring(p + 1).trim();
Credentials credentials = new SimpleCredentials(login, password.toCharArray());
adminSession = repository.login(credentials);
if (null != adminSession) {
// means authenticated and do your stuff here
}
}
}
}
}
Also in the webservice code which is calling the servlet of publish, below is the code on how I am supplying the credentials in auth headers :
String authStr = usrname+":"+password;
// encode data on your side using BASE64
byte[] bytesEncoded = Base64.encodeBase64(authStr.getBytes());
String authEncoded = new String(bytesEncoded);
connection.setRequestProperty("Authorization", "Basic "+authEncoded);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write("jsondata={sample:jsoncontent}");
writer.close();

Related

AspNetCore: How to mock external authentication / Microsoft account for integration tests?

I have an OpenID Connect / OAuth2 server (IdP) in my application stack. IdP allows both local and external authentication.
I have integration tests covering most scenarios, but struggle to create a end-to-end test for an external authentication scenario. There are multiple external providers, but from my application perspective they are all using the same workflow over OpenID Connect, only have slight difference (parameters, ie. redirect uri, scheme name, etc.). So it is enough to test one of them. One of them is Microsoft Account (aka. Azure AD)
Integration test is based on WebApplicationFactory (in-memory server with corresponding HttpClient). Local authentication is quite easy, because the whole part runs in my application domain, have access to full source code, etc. I simply create a request to the authorization endpoint and post back user credentials when prompted (I still need to parse the login page to retrieve the antiforgery token, but that's doable)
But when it comes to external, for example Microsoft Account, login involves multiple steps via AJAX and the final post with over 10 parameters, which I unable to reverse engenineer. Other provider has also same level of difficulty.
Since external providers are just blackboxes, from my IdP's perspective, it's just issuing a challenge (redirect to external authorization) and pick up after redirect. Is there a good way to mock the "in between" part?
My solution was to create a middleware, which will mock the external authentication. And then re-configure options for the external authentication scheme to direct to the path middleware is handling. You may also want to overwrite the signingkey (or turn of signature validation). So this code goes to WebApplicationFactory's ConfigureServices/ConfigureTestServices (etc., depending on your setup), to override original setup:
services.AddTransient<IStartupFilter, FakeExternalAuthenticationStartupFilter>();
services.Configure(AuthenticationSchemes.ExternalMicrosoft, (OpenIdConnectOptions options) =>
{
options.Configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = FakeExternalAuthenticationStartupFilter.AuthorizeEndpoint,
};
options.TokenValidationParameters.IssuerSigningKey = FakeExternalAuthenticationStartupFilter.SecurityKey;
});
Remark: WebApplicationFactory does not provide a way to override IApplicationBuilder (middleware) stack, so need to add IStartupFilter
The middleware then needs to issue a token with the security key and issue a form post back to the redirect uri. The usual way to achieve this to return simple HTML page with a form which will submit itself once loaded. This works fine in browsers, but HttpClient won't do anything, so the test have to parse the response and create a post request manually.
While this is doable, I wanted to spare this extra step, having to parse respond and re-send it, and make it a single step. Difficulties were:
redirect is not possible (starts as GET request, should ended as POST, need also form data)
cookies issued by OpenIdConnectHandler before redirecting (correlation and nonce) necessary to restore state, only available at redirect uri path (Set-Cookie with path=)
My solution was creating a middleware handling authorization (GET) requests at the same path as the redirect uri is set up, issue token and rewrite request so that OpenIdConnectHandler would pick up. Here's middleware's Invoke method:
public async Task Invoke(HttpContext httpContext)
{
if (!HttpMethods.IsGet(httpContext.Request.Method) || !httpContext.Request.Path.StartsWithSegments(AuthorizeEndpoint))
{
await _next(httpContext);
return;
}
// get and validate query parameters
// Note: these are absolute minimal, might need to add more depending on your flow logic
var clientId = httpContext.Request.Query["client_id"].FirstOrDefault();
var state = httpContext.Request.Query["state"].FirstOrDefault();
var nonce = httpContext.Request.Query["nonce"].FirstOrDefault();
if (clientId is null || state is null || nonce is null)
{
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
var token = CreateToken(clientId, state, nonce); // CreateToken implementation omitted, use same signing key as used above
httpContext.Request.Method = HttpMethods.Post;
httpContext.Request.QueryString = QueryString.Empty;
httpContext.Request.ContentType = "application/x-www-form-urlencoded";
var content = new FormUrlEncodedContent(new Dictionary<string, string>()
{
["id_token"] = token,
["token_type"] = "Bearer",
["expires_in"] = "3600",
["state"] = state,
});
using var buffer = new MemoryStream();
await content.CopyToAsync(buffer, httpContext.RequestAborted);
buffer.Seek(offset: 0, loc: SeekOrigin.Begin);
var oldBody = httpContext.Request.Body;
httpContext.Request.Body = buffer;
await _next(httpContext);
httpContext.Request.Body = oldBody;
}

ServiceStack API aspnet core with Azure AD B2C returns 401 for request even with bearer token

I have a working ServiceStack API that authenticates against a AzureAD tenant. We are trying to move this to start using Azure B2C. The application is build with c# and runs on net 5.0. I've managed to change the configuration to use the 'correct' config. I'm then using Postman to get my access token from my tenant suing the authorization code flow.
However, when i make a request to the api, the response is always a 401 status code.
Where in the servicestack code can I put a break point to see why this failure is happening? I have tried multiple places in our AppHostConfigurator.cs/AppHost.cs files, but the break points doesn't appear to display why a 401 is being sent back as a response. I'm sure it's something related to wrong claims/roles expected etc, maybe the Azure ADB2C application being setup incorrectly, but obviously i need to know exactly so that i can resolve.
I'm setting up the authentication like this:
private static void ConfigureAuthentication(IAppHost host)
{
var authProviders = new List<IAuthProvider> {new NetCoreIdentityAuthProvider(host.AppSettings)};
if (host.AppSettings.GetAllKeys().Contains("AzureAdB2C"))
{
var debugMode = host.AppSettings.Get(nameof(HostConfig.DebugMode), false);
var azureSettings = host.AppSettings.Get<AzureAdB2COptions>("AzureAdB2C");
var jwt = azureSettings.GetB2CJWTProviderReader(debugMode);
jwt.PopulateSessionFilter = (session, payload, request) =>
{
if (session.Email == null && payload.ContainsKey("upn") && payload["upn"].Contains("#"))
session.Email = payload["upn"];
if (session.UserName == null && payload.ContainsKey("unique_name"))
session.UserName = payload["unique_name"];
};
authProviders.Add(jwt);
}
var auth = new AuthFeature(() => new AuthUserSession(), authProviders.ToArray())
{
HtmlRedirect = "/account/signin",
HtmlLogoutRedirect = "/account/signout",
IncludeAssignRoleServices = false,
IncludeRegistrationService = false
};
// remove default service authentication services
auth.ServiceRoutes.Remove(typeof(AuthenticateService));
host.Plugins.Add(auth);
}
We are using swagger as well to call the API (which works as expected). This question is more about that requests that are submitted with a bearer token.
thanks
Please refer to this existing answer for examples of how to validate why a 3rd Party JWT Token is invalid with ServiceStack's JWT Auth Provider.

Extracting Kerberos Ticket from WCF method OperationContext

I have a WCF REST-enabled web service using Kerberos authentication. The OperationContext contains everything I would expect to receive from ADFS. However, how can I extract the actual Kerberos ticket? I need this ticket to generate a KerberosReceiverSecurityToken so that I can communicate with ADFS.
Using WireShark I am able to see the ticket at the HTTP level. Do I need to implement a custom HTTP handler to extract the ticket?
Thanks for the help!!
foreach(var header in HttpContext.Current.Request.Headers)
{
string headerVal = HttpContext.Current.Request.Headers[header];
if(headerVal.StartsWith("Negotiate"))
{
string parts[] = headerValStr.Split(' ');
string kerberosStr = parts[1];
//if a header token begins with "YII" its kerberos
//otherwise its likely NTLM (or other)
if(kerberosStr.StartsWith("YII"))
{
retVal = Convert.FromBase64String(kerberosStr);
break;
}
}
}

Alternative to cookie based session/authentication

Is there an alternative to the session feature plugin in servicestack? In some scenarios I cannot use cookies to match the authorized session in my service implementation. Is there a possibility to resolve the session using a token in http header of the request? What is the preferred solution for that in case the browser is blocking cookies?
I'm using ServiceStack without the built-in auth and session providers.
I use a attribute as request filter to collect the user information (id and token), either from a cookie, request header or string parameter.
You can provide this information after the user takes login. You append a new cookie to the response and inject the id and token info on clientside when rendering the view, so you can use for http headers and query parameters for links.
public class AuthenticationAttribute : Attribute, IHasRequestFilter
{
public void RequestFilter(IHttpRequest request, IHttpResponse response, object dto)
{
var userAuth = new UserAuth { };
if(!string.IsNullOrWhiteSpace(request.GetCookieValue("auth"))
{
userAuth = (UserAuth)request.GetCookieValue("auth");
}
else if (!string.IsNullOrEmpty(request.Headers.Get("auth-key")) &&
!string.IsNullOrEmpty(request.Headers.Get("auth-id")))
{
userAuth.Id = request.Headers.Get("id");
userAuth.Token = request.Headers.Get("token");
}
authenticationService.Authenticate(userAuth.Id, userAuth.token);
}
public IHasRequestFilter Copy()
{
return new AuthenticationAttribute();
}
public int Priority { get { return -3; } } // negative are executed before global requests
}
If the user isn't authorized, i redirect him at this point.
My project supports SPA. If the user consumes the API with xmlhttprequests, the authentication stuff is done with headers. I inject that information on AngularJS when the page is loaded, and reuse it on all request (partial views, api consuming, etc). ServiceStack is powerful for this type of stuff, you can easily configure your AngularJS app and ServiceStack view engine to work side by side, validating every requests, globalizing your app, etc.
In case you don't have cookies and the requests aren't called by javascript, you can support the authentication without cookies if you always generate the links passing the id and token as query parameters, and pass them through hidden input on forms, for example.
#Guilherme Cardoso: In my current solution I am using a PreRequestFilters and the built-in session feature.
My workflow/workaround is the following:
When the user gets authorized I took the cookie and send it to the client by using an http header. Now the client can call services if the cookie is set in a http-header (Authorization) of the request.
To achieve this I redirect the faked authorization header to the cookie of the request using a PreRequestFilter. Now I am able to use the session feature. Feels like a hack but works for the moment ;-)
public class CookieRestoreFromAuthorizationHeaderPlugin : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.PreRequestFilters.Add((req, res) =>
{
var cookieValue = req.GetCookieValue("ss-id");
if(!string.IsNullOrEmpty(cookieValue))
return;
var authorizationHeader = req.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.ToLower().StartsWith("basictoken "))
{
var cookie = Encoding.UTF8.GetString(Convert.FromBase64String(authorizationHeader.Split(' ').Last()));
req.Cookies.Add("ss-id",new Cookie("ss-id",cookie));
req.Items.Add("ss-id",cookie);
}
});
}
}

MVC 4 Application, EF and Web API Structure and User Authentication

I am developing a web site using the following technologies:
MVC 4
EF 5
Web Api
Future - possible Windows Phone/Windows 8 application.
I am using Web API so that I have a developed api that I can use on other clients.
However, I will need to authorise the user each time a request is made to the API. My initial thought was to do this via the HTTP headers. However, I'm just wondering if I should just use MVC Controllers instead of Web API for the MVC application and create a RESTful api if I was to develop a phone/win 8 application, again the user would need to be authenticated. So the originally problem still exists.
What are people's thoughts? Can any one point me to a tutorial on how I could securely pass the authenticated users details over the HTTP Header, also something that's a step by step tutorial as I'm going into this from scratch and need to understand it.
I use basic authentication to pass the credentials for authorization. This puts the credentials in the header. To do this is pretty straight forward by using the beforeSend event handler of the JQuery ajax function. Here is an example of how to do this.
getAuthorizationHeader = function (username, password) {
var authType;
var up = $.base64.encode(username + ":" + password);
authType = "Basic " + up;
};
return authType;
};
$.ajax({
url: _url,
data: _data,
type: _type,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", getAuthorizationHeader(username, password));
},
success: ajaxSuccessHandler,
error: ajaxErrHandler
});
This encodes the username/password that is sent in the header. Note that this is not enough security to rely on just the encoding as it is easy to decode. You still want to use HTTPS/SSL to make sure the information sent over the wire is secure.
On the Web API side you can make a custom AuthorizeAttribute that gets the credentials from the header, decodes them, and performs your authorization process. There is a separate AuthorizeAttribute used by the Web API as opposed to the controller. Be sure to use System.Web.Http.AuthorizeAttribute as your base class when creating your custom AuthorizeAttribute. They have different behaviors. The one for the controller will want to redirect to the logon page whereas the one for the Web API returns an HTTP code indicating success or failure. I return an HTTP code of Forbidden if authorization fails to distinguish a failure due to authorization as opposed to authentication so the client can react accordingly.
Here is an example method for getting the credentials from the header that can be used in the custom AuthorizeAttribute.
private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password)
{
bool gotIt = false;
username = string.Empty;
password = string.Empty;
IEnumerable<string> headerVals;
if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
{
try
{
string authHeader = headerVals.FirstOrDefault();
char[] delims = { ' ' };
string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
if (authHeaderTokens[0].Contains("Basic"))
{
string decodedStr = SecurityHelper.DecodeFrom64(authHeaderTokens[1]);
string[] unpw = decodedStr.Split(new char[] { ':' });
username = unpw[0];
password = unpw[1];
}
gotIt = true;
}
catch { gotIt = false; }
}
return gotIt;
}
And here is the code for decoding the header data that is used in this method.
public static string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
Once you have the username and password you can perform your authorization process and return the appropriate HTTP code to the client for handling.
Updated 3/8/2013
I wrote a blog post that goes into more details on how to implement this with SimpleMembership, the default membership provider for MVC 4 Internet Applications. It also includes a downloadable VS 2012 project that implements this.