Closed bickelj closed 1 year ago
See also PR #124 in the service repository.
@jim-mcgowan asked some questions, to which @bickelj and I now have answers:
Q: Do I create my own user account, or is it created for me?
A: The PDC team creates accounts, at least for now, and we do it in Keycloak, since Keycloak is our user management system. Keycloak is also where we add the phone numbers that Twilio will send texts to.
Q: If a user forgets their password, what do they do? Do they email the PDC team for help, or can the system help them?
A: The system can walk them through a password reset. They click "I forgot my password", and this causes two things to happen: an email with a one-time password reset link gets sent to the email address they supply (which must match the email address we have on record), and a text message with a code gets sent to their phone number of record. They go to the one-time link, and in order to reset their password there they of course must enter the code that was texted to them.
I'm not sure if folks have already found this resource, but I found the blog post Two-Factor Authentication with SMS in Keycloak and the associated dasniko/keycloak-2fa-sms-authenticator repo to be quite helpful.
@jasonaowen My first pass is using dasniko's work, yes!
W00t public resources FTW!
To test (step 4 above) I am using a static single page that uses keycloak.js
from https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter. I am serving it via the nginx container at a /ui
path, which requires putting the static content in a directory that nginx owns and mounting it via docker volume for that container in the compose.yml
. Again, this is for local development and testing. We don't have to use the keycloak.js
library nor serve static content from our reverse proxy container when we go to production. This seemed like the straightforwardest way to (locally) run the full login workflow with keycloak.
```
The next step is to get the jar to log a message successfully during a browser login (using that test flow).
Test succeeded (finally). To get it to succeed, there are several pre-requisites:
/providers
directory (I used env var plus a docker volume to do this)./providers
directory (env var plus docker volume again).I see the expected SMS template and required OTP entry after setting up a mobile number on a first authentication flow. On that first login where the user enters an SMS it does not appear to require the OTP via SMS. See https://github.com/dasniko/keycloak-2fa-sms-authenticator/discussions/29 for explanation.
Regardless, the next step is to use code that calls the SMS API in place of a logger message.
Code is in place but the real next step is to get Twilio and its dependencies on keycloak's classpath too.
I tried a shaded jar using the gradle shadow plugin with some excludes for the jars I already see on keycloak's classpath and that seemed to work. To be a little less brittle might require relocations as well. Next step is to figure out relocations with the shadow plugin.
Progress commits can be seen in #3.
A first attempt at relocation of packages in the fat/shaded jar reveals a problem:
I can't tell immediately if it means that the classes I am including in this jar, i.e. woodstox, are being used not only by other classes included in the jar but also (unintentionally) being used by the whole of keycloak, or if it means I need to further change the configuration to relocate the SPI declarations as well. I'm assuming the latter.
Edit/Update: three things helped. First, woodstox implements several standard SPIs that already have their own implementations on the classpath in keycloak, so I excluded woodstox. Second, I found the commons-logging jboss bridge/adapter in keycloak so I excluded commons-logging. Third, a function in the shadow jar mergeServiceFiles()
makes sure that the SPI descriptors get relocated too. When looking inside the jar now (jars are zip files), there is only one root package of org.philanthropydatacommons
, with two packages in there, auth
(the code here), and shadow
(all the dependencies), and the contents of META-INF/services
refer only to what we want: org.keycloak.authentication.AuthenticatorFactory
(the keycloak SPI implemented by this software) and the rest are shaded as org.philanthropydatacommons.shadow.[yadayadayada]
(avoiding conflict with other SPIs on the keycloak classpath).
(Appreciating the detailed notes here, @bickelj, FWIW!)
Step 5 is (more) done with the merge of https://github.com/PhilanthropyDataCommons/auth/pull/3, the next step is "Clean up, expand use of, and merge the code in https://github.com/PhilanthropyDataCommons/service/pull/124 so that we can enable use by the service.".
Cool! Thanks, @bickelj.
The quoting/linking is a bit borngled above? The reference is to service#124, if I understand correctly.
@kfogel Fixed! That was a rushed comment. The real next step was to copy a working jar to our test and production machines to make sure we don't forget where it is and document said copying. That is in progress. Then the next step is "Clean up, expand ... service#124".
Task number 6 (Update domain names as needed) is still ongoing. The first part, changing to api.[yada]
is complete in both test and prod, but the second part for auth.[yada]
still needs to be changed in prod. that is the next step.
Task number 8 (Clean up, expand use of, and merge the code in https://github.com/PhilanthropyDataCommons/service/pull/124 so that we can enable use by the service) is also ongoing, paused on an eslint rule violation in pending test code.
Task number 6 (Update domain names as needed) is completed. Next is task number 8 (Clean up, expand use of, and merge the code in https://github.com/PhilanthropyDataCommons/service/pull/124 so that we can enable use by the service).
While documenting and doing task 8 (Clean up, expand use of, and merge the code in https://github.com/PhilanthropyDataCommons/service/pull/124 so that we can enable use by the service), I found what I just shared in https://github.com/PhilanthropyDataCommons/deploy/issues/49#issuecomment-1457231323, and the next step is in the same. See update in the post.
I think back in November when developing the capability on the service side, I was running keycloak in a container, but I was accessing the auth services from outside the container, i.e. running via npm
on localhost:3000
. When actually integrating within docker, the service needs to be able to validate a JWT and issuer using the same URLs as the caller from outside the docker overlay.
Task 8 is stuck on a jest/supertest/jwks-mock issue, but task 9 is now there in the deploy
repo and in test environment.
I checked task (9) as done (even though it is still a manual delivery step) because the jars are in production and configured. I created a test user and did a login flow that correctly sent me an SMS OTP.
2FA is live in the pilot as of this week.
@bickelj is there anything more you want to do as part of this issue? Or should we close it as completed?
I think steps (10) and (12) are still useful, but we can consider those out of scope for the purposes of this issue.
Step (10) is now tracked in service#290. Step (12) should emerge naturally and can be in a separate ticket.
To provide a second factor of authentication via SMS, we'll find or write a keycloak provider/extension to send a one-time passcode via SMS to the authenticating user's mobile phone.
The workflow will begin with a keycloak authentication attempt and our extension will call twilio.
Tasks involved:
auth.[yada]
subdomain separately fromapi.[yada]
). See https://github.com/PhilanthropyDataCommons/deploy/issues/65 and https://github.com/PhilanthropyDataCommons/deploy/issues/66 for details..env
file andREADME
indeploy
project.There may be more tasks as well.