mod_authn_otp is an Apache web server module for two-factor authentication using one-time passwords (OTP) generated via the HOTP/OATH algorithm defined in RFC 4226. The developer's has listed only one compatible device (the Authenex's A-Key 3600) on their website. If a device is fully compliant with the standard, and it allows you to recover the token ID, it should work. However, without testing, it's hard to tell whether a device is fully compliant.
Have you ever tried other devices (software or hardware) with mod_authn_otp (or other open source server-side OTP program)? If yes, please share your experience :)
Any device that claims to be "OATH Compliant" should -- and probably does -- work.
The hard part is not compliance, it's getting the vendor to give you the secret key associated with the token. The don't like to do this because they make their money off the servers, not the tokens.
Note this new iPhone app also works if cell phones are an option for you.
If you're looking for more options, consider using mod-auth-radius or mod-ldap. Most two-factor auth solutions support radius and ldap and you will get far more options and flexibility. Plus, you can do things like run your radius auth through Active Directory and then have IAS/NPS proxy the request to the 2FA server. Thus, when a user is disabled in AD, they are disabled for 2FA too.
There are a couple of ways to do mod-radius:
https://www.wikidsystems.com/support/how-to/how-to-configure-apache-to-use-radius-for-two-factor-authentication-on-ubuntu// and https://www.wikidsystems.com/support/how-to/two-factor-authentication-for-apache-22-or-higher/
Related
For the first time, we are developing a new project that needs to support the browser (a website) and a mobile app built on react-native.
We have plenty of experience developing for the web, but little to no experience in the mobile app world.
We are now defining our API and trying to make it work for both platforms - This will be a basic JSON API, running on Flask. The API will have some public endpoints, and some protected ones as well (that require auth). We want a cross platform auth solution.
Typically, for the web, we have always relied on cookie based sessions (encrypted) that frameworks like Flask provide. We have been successfully using these in various scenarios: loading HTML templates directly from the web framework, or even when the frontend application runs on React (same domain + HttpOnly + secure cookies).
The introduction of react-native could potentially bring some new challenges, and some of our developers worry about not being able to continue to use our typical session based cookies - They are advocating to using something like JWT for authorization, for both the browser and app (and yes, I know that we can't compare sessions to token based auth).
I'm not a big fan of token based authorization (for a number of reasons, could entertain that conversation if anyone wants to waste some time...), and I can't seem to understand why we shouldn't stick to our normal encrypted "Cookie" value.
Here is how I imagine this could work, based on using the Flask cookie session:
A user hits /api/login with some credentials
The endpoint will validate the user and return back whatever response + the Set-Cookie header with a value of session=<encrypted string>
Any further requests from the browser/app would send back the cookie value again.
Essentially, nothing would change for typical browser implementations, this is how browsers work by default.
The react-native documentation mentions a few issues with cookie based authentication: https://reactnative.dev/docs/network#known-issues-with-fetch-and-cookie-based-authentication
From what I can gather (my knowledge is extremely limited on this subject), react-native might not always respect the Set-Cookie header. Which is OK - we would just have to make sure to send back the expected header with any further requests.
This, in theory, would fulfil our requirements.
I'm looking for feedback on my "solution", am I missing anything? Did I get HTTP completely wrong?
React Native actually provides networking similar to that of websites developed with basic JavaScript, which also supports cookie based authentication, however there are some serious caveats as mentioned in here
The following options are currently not working with fetch
redirect:manual
credentials:omit
Having same name headers on Android will result in only the latest one being present. A temporary solution can be found here:
https://github.com/facebook/react-native/issues/18837#issuecomment-398779994.
Cookie based authentication is currently unstable. You can view some of the issues raised here:
https://github.com/facebook/react-native/issues/23185
As a minimum on iOS, when redirected through a 302, if a Set-Cookie header is present, the cookie is not set properly. Since the redirect
cannot be handled manually this might cause a scenario where infinite
requests occur if the redirect is the result of an expired session.
Therefore, I would advise against using it as it is unstable and might give you a hard time dealing with it
I also suggest looking into frameworks that handle authentication for you, such as auth0
Hope you find this helpful :)
I am ashamed to admit that I burned four full days trying to get Spring Security 3.1 to play nicely with Google Sign-in in a standard JSF web application. Both are awesome frameworks in their own right but they seemed incompatible. I finally got it to work in some fashion but strongly suspect that I have missed some fundamental concept and am not doing it the best way.
I am writing an app that our helpdesk uses to track system testing during maintenance activities when our systems are down and cannot host the app, so it is hosted externally. Our Active Directory and IdP are down during this activity so I cannot use our normal authentication systems. Google Sign-in is a perfect solution for this.
Google Sign-in works great in the browser using Google Javascript libraries and some simple code. The browser communicates with Google to determine if the user is already signed in, and if not, opens a separate window where the user can submit credentials and authenticate. Then a small bit of Javascript can send a concise, ephemeral id_token returned from Google to the server which the server can use to verify the authentication independently with Google. That part was easy. The beauty is that if the user is already signed into Gmail or some other Google app, authentication has already happened and Google does not challenge the user again.
Spring Security works great on the server side to protect specified resources and authenticate a user with a username and password. However, in this case, we never see the username or password - the credentials are protected by secure communication between the browser and Google. All we know is whether or not the user is authenticated. We can get the Google username, but Spring Security expects credentials that it can use to authenticate, to a database, in-memory user base, or any other system. It is not, to my knowledge, compatible with another system that simply provides yea-or-nay authentication in the browser.
I found many good examples online that use Spring Boot with EnableOAuth2Sso (e.g. here) but surprisingly few that use Spring Security in a standard app server which does not support EnableOAuth2Sso, and those few did not show any solution I could discern.
Here is how I've done it. I followed Google's simple directions here to provide authentication in the browser. I added this code to the onSignIn() method to send the id_token to the server.
var xhr = new XMLHttpRequest(); // Trigger an authentication for Spring security
xhr.open("POST", "/<my app context>/j_spring_security_check", true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
var params = "profileID=" + profile.getId() + "&fullname=" + profile.getName() + "&email=" + profile.getEmail() + "&id_token=" + googleUser.getAuthResponse().id_token
+ "&j_username=" + profile.getEmail() + "&j_password=" + id_token;
xhr.send(params);
window.location.replace("/<my app context>/index.xhtml");
Unfortunately the Spring Authentication object, when passed to the AuthenticationProvider that I provided, did not contain anything but the j_username and j_password parameters as Authentication.getPrincipal() and Authentication.getCredentials(), but this is all I really needed. This is a bit of an abuse of those parameters since I have set them to email and id_token, not username and password.
I wanted to pass the user's full name and email, which Google provides in Javascript as googleUser.getName() and googleUser.getEmail(), to the backend as well. Since Spring Security does not accommodate anything but the username/password, and I was using Primefaces/JSF, I used Primefaces RemoteCommand to call a method on the backing bean with this information. This also feels a little clumsy.
In addition, I had to use window.location.replace() (in code above) because Spring Security did not redirect to my index.xhtml page as expected when I set this in the context with:
<security:form-login login-page='/login.xhtml' authentication-failure-url="/login.xhtml?error=true" default-target-url="/index.html" always-use-default-target="true" />
I have no idea why this does not work.
However, the app does now behave as I want in that it authenticates the user and the authenticated user can access the resources specified in Spring Security, and I wanted to share this in case anyone is doing a similar thing. Can anyone suggest a cleaner/better way? Thanks in advance.
i have a scenario running with an own CALDAV-server and CALDAV-clients like (iOS-calendar, mac-Calendar, Android sync adapter, Thunderbird/Lightning, Outlook Sync, ...)
The authentication so far works via basic auth (https and the "Authentication"-Header).
The CALDAV-clients store the user/password in their configuration.
So far so good, but the issue comes now once the password of the user/account either gets changed, reset, expired, etc.
The server has a restrictive password policy enforced, which locks the account after x failed attempts (e.g. 10).
What is happening now obviously is, that once the CALDAV-client configuration was not updated it continues to use an old password.
The server responds with an 401 not authorized - ok, thats fine apparently again.
But the Clients still continue to use the outdated password. It would be nicer to stop polling and present the user with a dialog that his credentials are not valid anymore. But the clients are out of my control so nothing can be directly done here.
The result: after 2-3 iterations (as most clients tries multiple request in one sync iteration) the account on the server of the user is locked due to too many failed login attempts.
That is not nice. The issue seems to be generic and known as "stale passwords".
A solution could only be a better client handling (out of scope here) or a oAuth-token handling. But i was not able to find anything that standard CALDAV-clients supports this. Only google calendar seems to enforce an oAuth2 authorization before allowing CALDAV communication.
So the question is, is there a good way to improve the bad experience of locked accounts?
Some special 401 response which tells the clients to forget the password or not using it again?
constructive feedback highly welcome.
Edit:
for macOS and ios calendar i found a strange behavior (bug) causing and/or enforcing the described situation.
A standard 401 response will cause the clients to bring up the password dialog as expected and described above. The clients stop polling until a new password is entered - as desired.
In my case the 401 response body contained an inline base 64 image (img src="data..."):
This doesnt lead to a password renewal dialog! Just a "something goes wrong" error state.
The clients are continuing to poll! Locking the accounts after some tries ;(
A solution for this problem than will be to remove the inline image but for me it sounds like a bug that an inline image in the 401 response provokes a different behavior on the client.
Some special 401 response which tells the clients to forget the password or not using it again?
Well, 401 is that response. If the client receives a 401 it knows the the login/password combination it provided doesn't work anymore, and shouldn't retry with the same. Obviously the clients don't do this, partially because:
On the other side your servers x-failed-attempts locking doesn't work with stateless protocols for obvious reasons. HTTP doesn't have that feature builtin. Locking the account is a side effect a client doesn't have to expect when running idempotent HTTP requests.
Assume the client is downloading 10 batches of items concurrently. If the credentials invalidate during this, the account would immediately be locked :-)
Summary: You can't use basic auth naively with backends that lock accounts after n-tries.
Google and iCloud both use token based auth schemes (Google OAuth, iCloud a proprietary one). You can't expect those to work in other clients. E.g. while the Apple clients support OAuth for Google, I don't think they support that for other account types.
So what can you do
I'm reading your question so that you own the account server and that the account locking is intentional and desired. (I.e. it is not a side effect of a different (e.g. SSO) backend system you reach out to.)
I think in this case it should be reasonable to rework your account system to allow unlimited login attempts with just the old password.
The lock-after-n-attempts measure is to protect against people trying different passwords. In your case it is always the same and as a bonus it also matches the old password.
There are a lot of different variations of this approach.
I would like to understand something please.
I have an application based on oAuth2 with Google Accounts.
So, teh first time I connect to this website, I am redirected to the authentication page on Google domain. So I type my email and password and I dont check "trusted computer" (or "remember me", I dont remember the exact term).
The thing is if I reboot my computer or even delete my cookie (but not my history (tested with Chrome on Android phone), I am not prompted again for the authentication and I have directly access to the application.
I would like to understand why ?
If somebody can explain it to me that should be great !
Thank you
You can actually force re-authentication in the Google OAuth api by passing &max_auth_age=0 to the auth URL.
Source:
Use the PAPE extension for further control of user authentication (optional)
Use the max_auth_age parameter in the PAPE extension to ensure that the login session of the user at Google is recent. You may also specify max_auth_age=0 to force a password reprompt.
https://developers.google.com/accounts/docs/OpenID
It's a bit confusing because they talk about OpenID, but I'm doing this successfully with Google's provided OAuth2 libs.
The Google OAuth 2 API really doesn't give you a way to force re-authentication. Lots of people have asked for this capability though, and maybe we should provide it.
It's hard to say, since it depends on what the flow was that as being executed.
Generally (with oauth) you weren't being prompted for authentication. You were being prompted for authorisation. Once you've authorised, you won't be prompted again, provided of course that the browser/google have some sort of session in existence which identifies the user.
When you say "delete my cookie", which cookie?
Yo can try going to this page https://accounts.google.com/b/0/IssuedAuthSubTokens?hl=en_GB and revoke the permission. That should then cause a repeat prompt.
I would like to use an LDAP server (probably Apache directory) to manage logins and credentials for an application. From time to time the application needs to work offline (on a laptop) without a connection to the LDAP server.
What is the best way to replicate the credentials localy?
I have already thought about:
Using Mitosis to replicate the LDAP server on the laptop.
But it would be a quite "heavy" and complicated solution. Moreover Mitosis seems not be be finished yet.
Exporting the credentials as LDIF file that could be stored on the laptop.
But I would need a way to check that the LDIF file actually comes from the LDAP server (The file should include a kind of signature). Moreover I would like to reject LDIF files that haven't be updated for more than a week. It would be nice if I could avoid implementing signing and age check myself.
Any other ideas or tools that could help me?
Edited Edit: I had a look at Kerberos because the documentation of the Java-Kerberos-API seems to say that it is possible to use a cached ticket in a local cache and I thought this might be a solution for me. Moreover Kerberos can be added as plugin to Apache Directory.
But the Kerberos cache stores decrypted tickets (aiming at sharing them with other applications). I would need the crypted version of the ticket to be able to check the user password during an offline session. Conclusion: Kerberos doesn't offer a simple solution to my problem.
Knowing that it will be probably ok if the user have to log on once online before being able to log on offline, consider the following algorithm:
user provides your application with a (username + password)
application attempts to contact LDAP for authentication
working online? (e.g. connection successful)
application authenticates against LDAP using (username + password)
authentication succesful?
application stores or updates hash(password) as (cached_credentials) for (username) into local secure storage
application proceeds as authenticated [[STOP]]
authentication failed?
application proceeds as non-authenticated (incorrect credentials) [[STOP]]
working offline? (e.g. network error)
application attempts retrieve (cached_credentials) for (username) from local secure storage
(cached_credentials) exists AND more recent than (1 week)?
application compares (cached_credentials) against hash(password)
match?
application proceeds as authenticated [[STOP]]
no match?
application proceeds as non-authenticated (incorrect credentials) [[STOP]]
(cached_credentials) does not exist OR less recent than (1 week)?
application proceeds as non-authenticated (network error) [[STOP]]
This is (or was, IIRC), by the way, the same model employed by Windows NT+ for user authentication against domain controllers. Upon login an attempt is made to authenticate against the domain controller and create or update the local (cached) version of the user profile. If the domain controller is not available, the user is prompted to proceed with authentication against the credentials captured in the local (cached) profile (if one exists.)
EDIT
Yes, this is, in spirit, the same solution as copying an ldif file locally, except that you do not have to parse ldif when you're offline. :)
It is understood that you can store any additional attributes (permissions, etc.) in your cache
It is also understood that 'secure storage' is at least signed. :) You can do this easily enough with a SHA-1 hash and a secret, or you can use full-fledged cryptographic providers available on your platform (or in Java, if using Java.) You do not need to crypt it as long as no secret information is stored inside.
Here is the solution I decided to use (I have already described it in an edit to my question, but I would like to able to accept an answer to "close" the question):
As I have not found another solution, I decided to use an LDIF export, add a timestamp as comment at the beginning of the file and then sign the file. To sign the file I calculate an hash value (SHA-1) of the file + a secret key. The signature is added as comment at the beginning of the file. To check the signature I remove the first line of the signed file and recalculate the hash value.