Questions regarding my GSoC project - II

Hii @tmortagne ! :smile:

I created this thread to ask some specific questions and also to explain my approach regarding the project so that we are on the same page.

First of all, I should tell my plan for the UI, which may look something like this.
design1

We will take the username of an existing XWiki user as input here and show him the same screen with some info on the screen like “No credentials found” if it’s the first time on that browser when he clicks Authenticate.
When he now clicks Register, we will show him a message, something like a “Credentials added, now authenticate yourself” on the same screen. When the user then, clicks Authenticate we will show him http://server/xwiki/bin/view/Main/.
When the user clicks Skip WebAuthn, he will be shown the default form login. This is my proposal regarding the flow of events. Please tell if you disagree.
How should I go on implementing this type of UI? Using a velocity template?

Now, I will tell you about the URLs and the endpoints that will be used. When the user sets WebAuthn as an authenticator in xwiki.cfg, and clicks login, the URL will change from http://server/xwiki/bin/view/Main/ to http://server/xwiki/webauthn.

When the user clicks on Register, we will send a POST request to http://server/xwiki/webauthn/register with form data(XWiki username). After this, we get a JSON response which looks somewhat like this:

{
"success":true,
"request":{
"username":"DamianArado",  
"requestId":"ETJGtRenYaL7BHjPDau-0wsNYix2HaCGx4V68Kdg0ag",
"publicKeyCredentialCreationOptions": {
  "rp": {
    "name":"XWiki",
     "id":"localhost"
   },
  "user": {
    "name":"DamianArado",
    "id":"1SeQVG9ieFoJrGeyqXFPklCX9GWMVE5BL-ohpkcMsSQ"},
    "challenge":"OqxBgzUXiT5-IWASySdQSp74nnrK7eu0P0BKvgIm5z8",
    "pubKeyCredParams":[{"alg":-7,"type":"public-key"},{"alg":-8,"type":"public-key"},{"alg":-257,"type":"public-key"}],
  "excludeCredentials":[], 
  "authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},
  "attestation":"direct","extensions":{}
  }
 }

After receiving the response from the client we construct a public-key credential from the response and send that to http://server/xwiki/webauthn/register/finish which will return a RegistrationResult if successful and throw an exception if not. In case it’s successful:

{
"success":true,
"request":{
"username":"DamianArado",
"requestId":"ETJGtRenYaL7BHjPDau-0wsNYix2HaCGx4V68Kdg0ag",
"publicKeyCredentialCreationOptions":{
  "rp":{
    "name":"XWiki",
    "id":"localhost"
   },
  "user":{
    "name":"DamianArado",
    "id":"1SeQVG9ieFoJrGeyqXFPklCX9GWMVE5BL-ohpkcMsSQ"
   },
 "challenge":"OqxBgzUXiT5-IWASySdQSp74nnrK7eu0P0BKvgIm5z8",
"pubKeyCredParams":[{"alg":-7,"type":"public-key"},{"alg":-8,"type":"public-key"},{"alg":-257,"type":"public-key"}],
"excludeCredentials":[],
"authenticatorSelection":{"requireResidentKey":false,"userVerification":"preferred"},
"attestation":"direct","extensions":{}},
"sessionToken":"1FDDyvgBYWykyOQ8q-kYajrr9LBtWORB-3eiNfHcPWo"
},
"response":{
  "requestId":"ETJGtRenYaL7BHjPDau-0wsNYix2HaCGx4V68Kdg0ag",
  "credentialId":"z-doFbbAaYP-aGrnh_kM7ItNuIJdIGctivAXDfcHvmY"
}

Now, we will redirect the user to http://server/xwiki/webauthn and the credentials will be stored as dedicated xobjects in the XWiki User Profile under XWiki.WebAuthn xclass.

When the user has WebAuthn credentials stored (in XWiki User Profile) and clicks on Authenticate, we send a POST request to http://server/xwiki/webauthn/authenticate and get a JSON response like:

{
"success":true,
"request":{
"requestId":"Vy8nYDKf6wP8tSApt0RiCMtK1SMSZkeREjs0tS2Zk_M",
"publicKeyCredentialRequestOptions":{
"challenge":"L_I1b-bdlNhdd8nt3gvARNHgjYww29XQ_8QRmun2snE",
"rpId":"localhost",
"allowCredentials":[{
"type":"public-key",
"id":"z-doFbbAaYP-aGrnh_kM7ItNuIJdIGctivAXDfcHvmY"
}],
"userVerification":"preferred",
"extensions":{"appid":"https://localhost:8080"}},
"username":"DamianArado"
}

After this, we wrap it in an assertion request and send a POST request on http://server/xwiki/webauthn/authenticate/finish which will return a response somewhat like:

{
"success":true,
"request":{
"requestId":"Vy8nYDKf6wP8tSApt0RiCMtK1SMSZkeREjs0tS2Zk_M",
"publicKeyCredentialRequestOptions":{
"challenge":"L_I1b-bdlNhdd8nt3gvARNHgjYww29XQ_8QRmun2snE",
"rpId":"localhost",
"allowCredentials":[{
"type":"public-key","id":"z-doFbbAaYP-aGrnh_kM7ItNuIJdIGctivAXDfcHvmY"
}],
"userVerification":"preferred",
"extensions":{
"appid":"https://localhost:8080"
}},
"username":"DamianArado"
},
"response":{
"requestId":"Vy8nYDKf6wP8tSApt0RiCMtK1SMSZkeREjs0tS2Zk_M",
"credential":{
"id":"z-doFbbAaYP-aGrnh_kM7ItNuIJdIGctivAXDfcHvmY",
"response":{
"authenticatorData":"SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MFAAAAAQ",
"clientDataJSON":"eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiTF9JMWItYmRsTmhkZDhudDNndkFSTkhnall3dzI5WFFfOFFSbXVuMnNuRSIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMiLCJjcm9zc09yaWdpbiI6ZmFsc2V9","signature":"cd2Aw-4lA2AmhyioHiR04l8_kjvu6SN5_EWWS-jEN7E-d-mOHqZSJDGehvd_SkspkSnQ5lg5k9TCoXTghkFbJkvmCqfXBGp3-HN2Yz2QsgBoCRwGVrg1q3bN_BUlJcSFP_ZFepAjZFW7Rs6w1Uyg9Sq4kpKhT-90by62MUZsKPq-mXDKIN4jtiKWOzirm68EOQmoURQv7N7IaTIpJvkTeib_f7HVkFwJLR34phLwMLArg4iIyElNpf2PWNb4Vxucjw1MkO7yNYoilpI9OrSKL62VymhnNuVrbtpXukpY5ZafuDjnLRiOQME9XKGRGLd14_Tu9QqVZX71AOQ4AyK18g",
"userHandle":"1SeQVG9ieFoJrGeyqXFPklCX9GWMVE5BL-ohpkcMsSQ"
},
"clientExtensionResults":{
"appid":false
},
"type":"public-key"
}},"registrations":[{
"signatureCount":1,
"userIdentity":{
"name":"DamianArado",
"id":"1SeQVG9ieFoJrGeyqXFPklCX9GWMVE5BL-ohpkcMsSQ"},
"credential":{
"credentialId":"z-doFbbAaYP-aGrnh_kM7ItNuIJdIGctivAXDfcHvmY",
"userHandle":"1SeQVG9ieFoJrGeyqXFPklCX9GWMVE5BL-ohpkcMsSQ",
"publicKeyCose":"pAM5AQABAyBZAQDC_4GE6sPOysEwHJyeWy6a1n3llh8lCu_RwPsGpLnQs_7IUrV1AuVAkDssa4wKJU1Lv9RHhOqL21t2QzahFP2fg-n5F0Www2pb6RgXKfs2gU1aOgAyuhG7bBiWo-aEKaMxbUWklEgRxi2k4SW1qiv2d37xFJGM2yb4Qy6E_Rzt7OOvMCFYFz5J1nqfWUWnGnMALHS9qW-KHXpxe27PxCj4H3KcsVnUZdIam0HBknvD9HntT2khm_0G7dZA1Ehpl_ZXYTHtylFzq5OImcL_HwAUaqzOX01KlFNE_OAYNrfbyE2jFH5vGp9V95TOZ1jIDFqeZq2Y4hFA72PPcj93K95_IUMBAAE","
signatureCount":1
},
"attestationMetadata":{
"trusted":true
},
"username":"DamianArado",
"registrationTime":"2021-08-09T21:33:08.483Z"
}

We will then redirect the user to http://server/xwiki/bin/view/Main/.

Thomas, how should I handle these URLs? WDYT? You told me that " in practice you could also simply have if/else on the element which follow /webauthn/ in the URL.", but I can’t understand that.

And regarding the flow, please tell if I need to explain something more…or if my approach is incorrect.
The requests and responses that I mentioned above are the constraints due to the library, so I will have to implement it accordingly.

Thanks in advance, for your time!

I don’t see any mention of the user being automatically authenticated, which would be my assumption of what happen most of the time when coming back with the same browser. I expect to see a UI only for a new browser or if this browser is associated with several users for this website (unless this does not make sense).

This is generally implemented using a Velocity template yes, there is a good example in the OIDC Authenticator.

It’s not clear to me why you need to redirect the browser. In XWiki when a user explicitly click on the Log-in button he goes to http://server/xwiki/bin/login/XWiki/XWikiLogin. By default, this URL shows a form, but your authenticator can actually decide to send back a custom UI, that’s what happen in oidc/OIDCAuthServiceImpl.java at 641d29022234b7c9927aa8425ad088db2dadcad8 · xwiki-contrib/oidc · GitHub for example.

In general, I’m not really sure why you even have any custom endpoints. The goal of an endpoint is to implement a protocol in which a client is supposed to access a specific endpoint to do specific things but from what you explain here you seem to be the one deciding at every step to go from one URL to another, and it seems to me you could do all that by staying in the same URL (and simply have a different behavior because you send different parameters or content in the request).

I think you mean to say, if an existing XWiki user has not generated any WebAuthn credentials using his username, then only we should show him the UI. If he already has one or more credentials generated (due to different browsers), then we can just fetch these and authenticate him using public-key id and publickeycose (part of credentials in xobject for an XWiki user) and show him http://server/xwiki/bin/view/Main/. Now, the use-cases for this approach would be:

  • If a standard XWiki user has no WebAuthn credentials generated yet for his username, then only he will be shown this UI. When they will click on Register, we will create WebAuthn credentials for them. After this, now we can either:
    • Keep them on the same page.
    • Redirect them to http://server/xwiki/bin/view/Main/ (unauthenticated).
    • Redirect them to http://server/xwiki/bin/view/Main/ (authenticated).
      design2
  • If a standard XWiki user already has one or more WebAuthn credentials for his username. When he clicks login from top-right on http://server/xwiki/bin/view/Main/, he will not be shown any UI and will be authenticated automatically. This may create some problems IMO, for example:
    • What if I have more than one xwiki user: User A, User B, and User C. I created webauthn credentials for User A and User C, but not for User B. When I click login, it authenticated me as User C, but I wanted to be authenticated as User A instead. This is a problem if we don’t explicitly indicate the username upfront.
    • If we don’t have WebAuthn credentials generated for any of our xwiki users, we can generate for any one of them at a time, and later when we try to login, it authenticates us with only the lone xwiki user that has WebAuthn credentials instead of showing that we don’t have it generated for other user(s).

Henceforth, IMHO it would be better for us to show the below UI, when someone sets WebAuthn as an authenticator in xwiki.cfg:
design1

Okay, Thomas.


The use-cases for my proposed approach would be:

  • When a user clicks login they will be shown the above UI at http://server/xwiki/bin/login/XWiki/XWikiLogin. Now, when they click on Register, WebAuthn credentials would be generated for the XWiki user they indicate in the form (credentials would be browser-specific) and stored as custom xobjects in XWiki user profile. After this, we can either:
    • Keep them on the same page.
    • Redirect them to http://server/xwiki/bin/view/Main/ (unauthenticated).
    • Redirect them to http://server/xwiki/bin/view/Main/ (authenticated).
  • When the user clicks Authenticate, we will redirect them to http://server/xwiki/bin/view/Main/ (authenticated) if they have credentials already stored under their XWiki user profile for the specific browser. If they are on a new browser (and hence, don’t have any credentials for that), we can either:
    • Show them the same page indicating they “don’t have credentials for this browser”. This is similar to the Register use-case (This will be mentioned on the extensions page for reference).
    • Redirect them to http://server/xwiki/bin/view/Main/ (unauthenticated).

WDYT of my approach Thomas?

I’m anticipating that XWiki user profile on a browser A and bowser B would be two different things and in case we save the public-key for example on browser A, it would not be accessible to the xwiki user on browser B.
But if the xobjects remain the same on different browsers, we don’t need to worry about the scenario where we needed to make sure the user can authenticate using different browsers that support WebAuthn. We can just use the public-key and authenticate them using their corresponding private-key stored on the built-in platform authenticator (Pin/Password/Fingerprint) and thus, that will not depend upon browsers. Please tell if I am wrong regarding this. :sweat_smile:

You are right. The library didn’t mention anywhere in its documentation that we need to implement endpoints as such.

Okay Thomas, I will be using this approach. It will be something new and interesting for me. :grinning:

Clicking login is not my definition of automatic authentication. Again, with this kind of authenticator I would expect the user to not have to do anything at all to be authenticated.

Actually, what the authenticators usually do is redirect the user to wherever they were before clicking log-in or when accessing a page which require authentication.

Keep in mind that you are the WebAuthn protocol expert here, I’m just the XWiki one. That being said, having the same key for several browsers sounds very strange to me. For usual security concerns, I would expect each browser to be associated with a different one so that you can invalidate one you don’t use anyone or which you feel might be “stollen” by someone else. So my expectation would be that the user profile document contain a different custom WebAuthn xobject for each browser, and for a user profile which contain any xobject with the key provided by the browser.

Thomas, with WebAuthn, I hope you understood the problem that I emphasized with ‘automatic’ authentication: any user will be authenticated if we don’t explicitly indicate the username. But, do let me know what is the behavior that you expect to have here?

WebAuthn is an authenticator that is aiming to remove passwords (including remembering them (-_-)) from the picture, providing better security in general. This will provide a solution that is one of the most efficient in bypassing ‘phishing’ or ‘replay attacks’ for instance.


We just need a ‘username’ of an existing XWiki user to store credentials for them as xobjects. Then, we will check if we have an xobject already under the XWiki user profile of an existing standard XWiki user and authenticate them using those.

If anyone wants to check how it works, they can check: https://webauthn.org or https://webauthn.io.

Alright!

An XWiki expert is more important here obviously. :smile:

Thomas, If someone peeks at my XWiki instance or somehow manages to get hold of my public key for example, It would be useless for them as they still require the corresponding private key that is stored in the authenticator (in our case, it would be platform authenticators like PIN/Password/Fingerprint as well as a security key like a Yubikey for insane level of security). So, unless they know our primary authenticator, they can’t hack into our XWiki account. :grinning:


Having said that, we will also provide a feature later where the user can delete credentials under their username, and thereafter, they can generate new ones, if their account still gets compromised anyhow.

When I checked on my local machine, the xobjects for Open-ID connect provider are same in the XWiki user profile of my xwiki user when I log-in either on any of Chrome, Edge, or Firefox. So, I don’t think it would be much of an issue implementing the same thing wrt WebAuthn since I already told you a public key alone is pretty much useless, it still requires the private key, which is never exposed anywhere (it stays with the authenticator).

Your thoughts?

I did not talk about a stolen XWiki instance, but a browser. If someone manages to get a hand on one of your browsers already configured to access an XWiki instance, you will want to prevent that browser to authenticate, which is only possible if each browser is associated to a different xobject (so that you can simply delete the xobject).

In OIDC protocol we don’t care about the browser since nothing is stored on browser side. But it’s very different with webauthn where the browser itself is an identity provider.

I’m not talking about the different kind of browsers here, I’m talking about each HTTP client.

For example, you configured access to mywiki.com with a client A (on your home desktop), B (on work desktop) and client C (on your phone). If someone stole your phone (which does not have any general password or find it) you will want to prevent any future access from client C without having to recreate A and B (because it’s very annoying).

If someone gets a hand on one of my browsers, then I’ll delete the credential objects and then create new ones. Note that, if each browser is associated with a different xobject, we will have to generate credentials and store them as xobjects in each one of them which may be undesirable from the user’s pov. Here, using the same xobject with different browsers will be a better choice imho.

Thomas, the browser will just help us to implement the WebAuthn JS API calls: navigator.credentials.create and navigator.credentials.get. What acts as a sort of identity provider is our platform authenticator (built-in to our local machine, for ex., PIN/password or Fingerprint etc). So, even if they’ll get access to these, we’ll have the option to delete these xobjects.

Oh, okay.

Client A, B and C will have different xobjects for them. So, no issues if one gets compromised. While on a single client, xobjects will be same.

Well, this is what I keep asking for :slight_smile:

1 Like

Thanks, sorry for the confusion. :sweat_smile: