Basic authentication for website to secure site login and API access - node.js

I'm looking at the security model of a website that's being developed. After researching the web i have found that there are several security models to secure websites i.e. Basic Auth, JWT ...
At the moment, SSL is not enabled as still in dev. Website has a login page and communicates via API's (including login and logout). On the login page, as a test, I have attempted to log in with false details, and then I have looked at the developer tools to identify the security mechanism and found the following screenshots. I think the site is using basic authentication, though I noted that the email / password is not encoded and is using a custom login form. Could someone confirm if it is basic authentication being utilised?
Developer Tools Images
[Request Header Image][2]
UPDATE:
I discovered that once the user is authenticated by email/password, I should have posted the screenshots as this is where keys are returned. In the below screenshot a bidder token and bidder secret is sent back to client. I think these are generated through crypto on backend. So I don't think its JWT, but is this a suitable way in creating keys and not sending in header but in response body?
Network tab after user logged in
Login Form Code :
{
/* prepare ui */
progress.classList.remove('hide');
login_btn.innerText = 'Logging In';
login_btn.setAttribute('disabled', true);
/* make http request */
var http = new XMLHttpRequest();
var url = SERVER + '/api/bidder/login';
var body = {
email: email.value,
password: password.value
};
http.open('POST', url, true);
http.setRequestHeader('Content-type', 'application/JSON');
http.onreadystatechange = function () { //Call a function when the state changes.
if (http.readyState == 4 && http.status == 200) {
var res = JSON.parse(http.responseText);
if (res.status) {
localStorage.setItem("bidData", JSON.stringify(res.data));
window.location.href = window.location.href.replace('login.html','');
} else {
Toast.show('Danger', res.message);
}
/* reset ui */
progress.classList.add('hide');
login_btn.innerText = 'Log In';
login_btn.removeAttribute('disabled');
}
}
http.send(JSON.stringify(body));
}

When you use basic access authentication, credentials wouldn't be loaded in a request payload. They reside in an authorization header like "Authorization: Basic ~some credential here~".
So if you neither see this authorization header in your request nor a popup like below on the website, basic access authentication is not enabled.

Spring security is the most basic authentication in the Spring project
If you want to enable Spring security, the first thing you must add is the spring security library to your project. After adding it, you create a class to configure Spring security.
A function in the class config for Spring security.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/",
"/api/statistical/**",
"/static/**",
"/webjars/**",
"/img/**",
"/css/**",
"/js/**",
"/api/diary/**")
.permitAll()
.antMatchers("/api/auth/**")
.permitAll()
.antMatchers("/api/user/checkUsernameAvailability", "/api/user/checkEmailAvailability")
.permitAll()
.antMatchers(HttpMethod.GET, "/api/users/**") //, "/api/polls/**"
.permitAll()
.anyRequest()
.authenticated();

Related

Azure Functions app + Auth0 provider, getting 401 when calling API with auth token

I have read, and implemented local dev projects to match, Auth0's Complete Guide To React User Authentication with Auth0, successfully. I am confident in the implementation, given that all aspects of login and route protection are working correctly, as well as the local express server successfully authenticating API calls that use authentication tokens generated via the Auth0 React SDK.
I have added third button to the sample project's external-apis.js view for use in calling another API that I am trying to integrate with, which is an Azure Functions app. I would like to use Auth0 for this API in the same way I do for the express server, and take advantage of Azure's "Easy Auth" capabilities, as discussed in this MS doc. I have implemented an OpenID Connect provider, which points to my Auth0 application, in my Azure Function app per this MS doc.
This is what the function that calls this Azure Function app API looks like:
const callAzureApi = async () => {
try {
const token = await getAccessTokenSilently();
await fetch(
'https://example.azurewebsites.net/api/ExampleEndPoint',
{
method: 'GET',
headers: {
'content-type': 'application/json',
authorization: `Bearer ${token}`,
},
}
)
.then((response) => response.json())
.then((response) => {
setMessage(JSON.stringify(response));
})
.catch((error) => {
setMessage(error.message);
});
} catch (error) {
setMessage(error.message);
}
};
My issue is that making calls to this Azure Function app API always returns a 401 (Unuthorized) response, even though the authorization token is being sent. If I change the Authorization settings in the Azure portal to not require authentication, then the code correctly retrieves the data, so I'm confident that the code is correct.
But, is there something else I have missed in my setup in order to use Auth0 as my authentication provider for the backend in Azure?
Through continued documentation and blog reading, I was able to determine what was missing from my original implementation. In short, I was expecting a little too much after reading about tge "Easy Auth" features of Azure, at least when using an OpenID Connect provider like Auth0. Specifically, the validation of the JSON Web Token (JWT) does not come for free, and needed further implementation.
My app is using the React Auth0 SDK to sign the user in to the identity provider and get an authorization token to send in its API requests. The Azure documentation for client-directed sign-in flow discusses the ability to validate a JWT using a specific POST call to the auth endpoint with the JWT in the header, but even this feature seems out of reach here, given that OpenID Connect is not listed in the provider list, and my attempts at trying it anyway continued to yield nothing but 401s.
The answer, then, was to implement the JWT validation directly into the Azure function itself, and return the proper response only when the JWT in the request header can be validated. I would like to credit blog posts of Boris Wilhelm and Ben Chartrand for helping to get to this final understanding of how to properly use Auth0 for an Azure Functions backend API.
I created the following Security object to perform the token validation. The static nature of the ConfigurationManager is important for caching the configuration to reduce HTTP requests to the provider. (My Azure Functions project is written in C#, as opposed to the React JS front-end app.)
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
namespace ExampleProject.Common {
public static class Security {
private static readonly IConfigurationManager<OpenIdConnectConfiguration> _configurationManager;
private static readonly string ISSUER = Environment.GetEnvironmentVariable("Auth0Url", EnvironmentVariableTarget.Process);
private static readonly string AUDIENCE = Environment.GetEnvironmentVariable("Auth0Audience", EnvironmentVariableTarget.Process);
static Security()
{
var documentRetriever = new HttpDocumentRetriever {RequireHttps = ISSUER.StartsWith("https://")};
_configurationManager = new ConfigurationManager<OpenIdConnectConfiguration> (
$"{ISSUER}.well-known/openid-configuration",
new OpenIdConnectConfigurationRetriever(),
documentRetriever
);
}
public static async Task<ClaimsPrincipal> ValidateTokenAsync(AuthenticationHeaderValue value) {
if(value?.Scheme != "Bearer")
return null;
var config = await _configurationManager.GetConfigurationAsync(CancellationToken.None);
var validationParameter = new TokenValidationParameters {
RequireSignedTokens = true,
ValidAudience = AUDIENCE,
ValidateAudience = true,
ValidIssuer = ISSUER,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
IssuerSigningKeys = config.SigningKeys
};
ClaimsPrincipal result = null;
var tries = 0;
while (result == null && tries <= 1) {
try {
var handler = new JwtSecurityTokenHandler();
result = handler.ValidateToken(value.Parameter, validationParameter, out var token);
} catch (SecurityTokenSignatureKeyNotFoundException) {
// This exception is thrown if the signature key of the JWT could not be found.
// This could be the case when the issuer changed its signing keys, so we trigger
// a refresh and retry validation.
_configurationManager.RequestRefresh();
tries++;
} catch (SecurityTokenException) {
return null;
}
}
return result;
}
}
}
Then, I added this small bit of boilerplate code toward the top of any HTTP-triggered functions, before any other code is run to process the request:
ClaimsPrincipal principal;
if ((principal = await Security.ValidateTokenAsync(req.Headers.Authorization)) == null) {
return new UnauthorizedResult();
}
With this in place, I finally have the implementation I was looking for. I'd like to improve the implementation with something more generic like a custom attribute, but I'm not sure that's possible yet either for OpenID Connect providers. Still, this is a perfectly acceptable solution for me, and gives me the level of security I was looking for when using a React front-end with an Azure Functions back-end.
Cheers!

Azure App Service with websockets and AD authentication

we got an application deployed as App Service and we are using SignalR for communication. After enabling AAD authentication - in browsers we started receiving 302 responses with redirect location to Azure AD.
Seems like the authentication layer on App Service is ignoring access_token passed by query string.
Request
Request URL: wss://<url>/hubs/chat?access_token=<token>
Request Method: GET
Response
Status Code: 302 Redirect
Location: https://login.windows.net/common/oauth2/authorize?...
After looking everywhere we couldn't find any solution to make this work.
The only solution to this issue that we see is either to disable authentication on App Service or use Long-Pooling, but both options are not acceptable in our situation.
By default, you web application will not get the access token from query string. Commonly, it will get the access token from authorization header or the cookie.
To get the access token from query string, you need to implement your custom authentication way.
Install Microsoft.Owin.Security.ActiveDirectory NuGet package.
Create an authentication provider which will get access token from query string.
public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
public override Task RequestToken(OAuthRequestTokenContext context)
{
var value = context.Request.Query.Get("access_token");
if (!string.IsNullOrEmpty(value))
{
context.Token = value;
}
return Task.FromResult<object>(null);
}
}
Add map in .
app.Map("/yourpath", map =>
{
map.UseWindowsAzureActiveDirectoryBearerAuthentication(new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Provider = new QueryStringOAuthBearerProvider(),
Tenant = tenantId,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = clientId
}
});
map.RunSignalR(hubConfiguration);
});
After multiple calls with Microsoft Technical Support, MS confirmed that App Service Authentication layer doesn't support access token passed in query string and there are no plans for this support yet. So there are two options:
Use different protocol for SignalR (long pooling works just fine)
Drop App Service Authentication
Using a custom middleware, I was able to update the request prior to authorization occurring:
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace Stackoverflow.Example.Security.Middleware
{
public class BearerTokenFromQueryToHeaderMiddleware
{
private readonly RequestDelegate _next;
public BearerTokenFromQueryToHeaderMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
var token = context.Request.Query["access_token"];
if (!string.IsNullOrWhiteSpace(token))
{
context.Request.Headers.Add("Authorization", $"Bearer {token}");
}
await _next(context);
}
}
}
I didn't try to get this working with the OpenID framework, but I did test using a custom policy. As long as this is registered earlier than the authentication, then this middleware should execute prior to the framework looking for the token in the header.

Identity Server 3 and Azure AD Single Sign Out

I'm using Identity Server 3 with WS-Federation plugin on top and Azure AD SAML2 Provider as an External Provider in Identity Server 3.
No Problems when I perform Single Sign On between IDSRV3, Client, Relying Party and AZURE AD.
The problem is when I perform Single Sign Out from Azure with authenticated RP (Ws Federation Client).
This is what I'm getting in Firefox:
Load denied by X-Frame-Options: https://localhost:5000/core/wsfed/signout does not permit cross-origin framing.
In Chrome:
Refused to display 'https://localhost:5000/core/wsfed/signout' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
As I see in the WsFederation plugin the "/wsfed/signout" route does not disable the XFO
[Route("signout")]
[HttpGet]
public async Task<IHttpActionResult> SignOutCallback()
{
Logger.Info("WS-Federation signout callback");
var urls = await _cookies.GetValuesAndDeleteCookieAsync(WsFederationPluginOptions.CookieName);
return new SignOutResult(urls);
}
But do on the "/wsfed" GET route:
[Route("")]
[SecurityHeaders(EnableCsp = false, EnableXfo = false)]
public async Task<IHttpActionResult> Get()
{
...
if (signout != null)
{
Logger.Info("WsFederation signout request");
return await ProcessSignOutAsync(signout);
}
Where is the mistake here? If I remove the X-Frame-Options headers in IIS directly it works fine but following the docs does not.
Any help is appreciated.

ServiceStack auth cookies different clients

I encouraged my company to use ServiceStack for one of the software projects. I am loving servicestack framework by all means. I came accross a problem that I couldn't figure out by myself.
In a web application i am using ServiceStack c# Jsonclient from a login page to authenticate. When i get authenticated c# client hold the ss-id cookies in it. So when i use same c# client for service calls i can access the session within my services.
But there is a autocomplete feature which calls a service by Jquery AJAX call the client there (browser) is not authenticated and browser does not hold ss-id cookie also.
My question is when i authenticate with c# client on code-behind. How can i store session cookies on browser (Is that needed?) so when i call service from javascript client i can access session in my services also.
Thanks for the response.
My question is when i authenticate with c# client on code-behind. How can i store session cookies on browser (Is that needed?)
So, your browser needs to have a session cookie to let ServiceStack know that it has been successfully authenticated. The browser knows nothing about what is happening with your C# clients. I'm not sure how you are posting your authentication data (username/password/etc) but if it is through a browser and you're handing the data off to a C# client you could do something like below. This is wihin MVC but the point is to get the session cookie out of the client and into the response to the browser.
public ActionResult Login()
{
var client = new JsonServiceClient("http://localhost");
var response = client.Post(new Auth() {UserName = "TestUser", Password = "Password"} );
var ssId = "";
foreach(Cookie c in client.CookieContainer.GetCookies(new Uri("http://localhost")))
{
if (c.Name == "ss-id")
{
ssId = c.Value;
}
}
var cookie = new HttpCookie("ss-id", ssId);
this.ControllerContext.HttpContext.Response.SetCookie(cookie);
return new EmptyResult();
}
If you are using MVC this would be a better way. However, I'm not sure your reasoning for using C# clients and how your are receiving the authentication data and your ability to get into the Response to the browser.
Setting both "ss-id" and "ss-pid" cookies works for me when authenticating the browser as well as the .NET client.
A somewhat rewritten part of my logon controller:
[HttpPost]
public ActionResult Logon(Auth auth)
{
using (var client = new ServiceStack.ServiceClient.Web.JsonServiceClient("://ServicestackUrl/"))
{
auth.provider = "credentials";
auth.RememberMe = true;
client.UserName = auth.UserName;
client.Password = auth.Password;
var authResponse = new AuthResponse();
try
{
authResponse = client.Send(auth);
}
catch (WebException ex)
{
throw ex;
}
foreach (Cookie c in client.CookieContainer.GetCookies(new Uri(client.BaseUri)))
{
if (c.Name == "ss-id" || c.Name == "ss-pid")
{
Response.SetCookie(new HttpCookie("ss-id", c.Value));
}
}
//Log the user on with forms authentication
string encryptedTicket = FormsAuthentication.Encrypt(
new FormsAuthenticationTicket(
1,
authResponse.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(FormsAuthentication.Timeout.Minutes),
false,
""
)
);
Response.Cookies.Add(
new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
);
}
//Do a redirect or something
return Redirect(GetRedirectUrl);
}

MVC Form based Spring Security login?

I have an application that uses Spring Security to control access to pages, to manage user roles (GrantedAuthority) and for ACL. The application uses the standard UsernamePasswordAuthenticationFilter that intercepts requests to /j_spring_security_check (with j_username and j_password request parameters), and using a ProviderManager it authenticates the user and on success stores it in the SecurityContextHolder.
The above is configured in the security context, using a customized UserDetailsService:
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref='myUserDetailsService'/>
</authentication-manager>
The above approach in my case is not optimal, for the following reasons:
Adding a captcha requires extra filters
In order to customize the login logic, I need to replace the AuthenticationProvider as well
showing errors in the login form is complex, since I cannot use Spring MVC's forms
My idea is to remove the interceptor based login and put all the logic inside a Spring 3 MVC controller. The pseudo-code is as following:
RequestMapping(value="/login/", method = RequestMethod.POST)
public String attemptLogin(HttpServletRequest request, HttpServletResponse response,
#ModelAttribute("login") LoginCmd login, Model model) {
// validate command (username, password, captcha)
// ...
// load user from DB
User user = userService.loadUserByUsername(login.getUsername());
// extra logic (check number of failed logins + other stuff)
// ...
// In case everything is fine, create a spring security User
/* Instead of creating the user, read it from DB */
org.springframework.security.core.userdetails.User authUser =
new org.springframework.security.core.userdetails.User(
login.getUsername() /*username*/,
login.getPassword() /*password*/,
true /*enabled*/,
true /*accountNonExpired */,
true /*credentialsNonExpired */,
true /*accountNonLocked*/,
new ArrayList<GrantedAuthority>() /*authorities*/
);
// build the AuthenticationToken
UsernamePasswordAuthenticationToken authResult =
new UsernamePasswordAuthenticationToken(authUser, login.getPassword(),
authUser.getAuthorities());
// use WebAuthenticationDetailsSource do build details
authResult.setDetails(detailsSource.buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authResult);
return SUCCESS_VIEW;
}
Do you see any problem with the solution here above? Is setting the authentication inside the SecurityContextHolder enough? Am I missing something?
Comments and suggestions are welcome ;-)
Thanks a lot to everyone
Andrea
I went through the Spring Security code, and on successful authentication also the original code just stores the Authentication object in the SecurityContextHolder, nothing else is done.
For example, in class AbstractAuthenticationProcessingFilter (which is used by the standard login intercepting requests to /j_spring_security_check) does that:
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authResult) throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher != null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
I implemented this on my application and everything works fine.

Resources