Open ignaloidas opened 5 years ago
At GitHub, we try to minimize the use of Javascript on the frontend. We have plain forms at the core of most forms of authentication (e.g. username, password, SMS, OTP). There are some good reasons to use JS for webauthn, but in principle it would be nice if it was possible to shed the overhead in favor of simplicity for the common case.
yeah, nominally ISTM this'd require defining some form of declaration for the webapp to make that it wishes to employ webauthn and possibly defining a common format for conveying the webauthn bits between the client platform and the RP front-end server.
from discussion on webauthn call today: so which RPs would use this "declaritive approach" rather than the webauthn API ? how many RPs would rather use the approach proffered here rather than the webauthn API ?
Hiya, I'm the one who asked @ignaloidas to suggest this on behalf of https://sourcehut.org. The inability to utilize this without JavaScript is a blocker for us, we will not add webauthn without it.
I've spent some time thinking about creating a declarative HTML API that eliminates javascript. In this case, a lot of configuration properties need to be defined by the RP, somewhere. (i.e. the PublicKeyCredentialCreationOptions, etc. ) I think this should be done with a combination of both a config document hosted at the RP (written in JSON, retrieved by HTTPS), and properties defined locally in the HTML element. The local property values override the values in the config document. The config document and/or the (rel="capis-api") attribute can also serve as the "declaration" that the RP supports the API. I use a single HTML attribute to hold a JSON-style set of properties. @equalsJeffH is right. The browser must support one or more transport formats. For the browser to send the credentials to the RP. I suggest using a JSON Web Signature (JWS) as the default. (The object needs to contain the "action", "challenge", user_id, etc.) Just as important, is that the RP must issue a response, which is not a web page. It must be in a standard format, because the browser must parse it to see if the action succeeded. (It should probably be a JSON document.) Note that the API directly tells the browser the login status at the RP. (The browser may keep a list of all logged in RPs, and provide a "logoutAll" button.)
<a href="/old_style_login_form.html" rel="capis-api" capis-cfg=' "action":"ua/login", "redirect_uri":"/after_login.html", "endpoint_uri":"/validate_JWS.json", "capis_doc_uri":"/capis_config.json", "challenge":"H3uz78_k...", "transport_format":"JWS" '>Login</a>
The href is to catch OLD browsers that do not support the API. The new API does not use the href. (It uses the endpoint_uri.) "action" holds the requested operation. (Supports "login", "logout", "prove-user-presence", etc.) "capis_doc_uri" is the URL of the config document. "challenge" is a base64url encoded binary value. endpoint_uri is the URL the browser needs to send the credentials (JWS) to. redirect_uri is the page the browser should redirect to after user authentication success. Note that "endpoint_uri" and "transport_format" are not necessary in the HTML, they can be defined in the config document.
I had a vastly different idea for how this could work:
<form method="POST" action="/exampleapp/webauthn/finish_assertion">
<input type="hidden" name="exampleapp_request_id" value="3a74f2b4-cff0-4f50-8076-2f5532a0d6f3"/>
<input type="webauthn.get" name="public_key_credential" value='{
"publicKey": {
"challenge": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs",
"allowCredentials": [{ "type": "public-key", "id": "Pzy_hxuR849qZbLAE8Vr4U3KPiIN6W10ssJOR55BMZQ" }],
}
}'/>
<input type="submit"/>
</form>
That is, a perfectly normal form with nothing special except the new <input type="webauthn.get">
suggested in OP. The exampleapp_request_id
field is an example application-specific reference to server-side state containing things like the username, a copy of the challenge, and probably where to redirect the user next. Of course the application could add any other fields to the form that they need, as usual.
The <input type="webauthn.get">
would probably render a button which starts the WebAuthn ceremony, and replace the button with a success/failure indicator after the ceremony finishes. When the form is submitted, this would send a POST as usual with the contents
POST /exampleapp/webauthn/finish_assertion
exampleapp_request_id=3a74f2b4-cff0-4f50-8076-2f5532a0d6f3&public_key_credential={"id":"K3xM080fiCDCkv412SdQ6--982rRf9i6NtDY0Jkv-AJZaCH9-MNs-ijV2y2OXKbxjzD3rXR05rmGF_jGQnXyPQ","response":{"authenticatorData":"xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7cBAAAANQ==","clientDataJSON":"eyJjaGFsbGVuZ2UiOiJwakpQTWFvUUFFSEhKb0NtR0lVd3EtclZBc0JzRDY3Q2pCVDhMYXJwcEtjIiwiY2xpZW50RXh0ZW5zaW9ucyI6e30sImhhc2hBbGdvcml0aG0iOiJTSEEtMjU2Iiwib3JpZ2luIjoiaHR0cHM6Ly9kZW1vLnl1Ymljby5jb20iLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0=","signature":"MEUCIHwdgFLpCi05C1SeqRIBEwM1iJr4HJu8H9YC49vlTd+OAiEAxGDHU0+zNrgk8eSrx9KD8bj6jqRiKdPNz2ANA7EShqI="},"clientExtensionResults":{},"transports":["usb"]}
or in the case of failure something like:
POST /exampleapp/webauthn/finish_assertion
exampleorg_request_id=3a74f2b4-cff0-4f50-8076-2f5532a0d6f3&public_key_credential={"error":"NotAllowedError","message":"The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission."}
This way we would reuse the existing JSON/JS data structures, and the only new thing to add would be how <input type="webauthn.create">
and <input type="webauthn.get">
work (and how to en/decode the binary values to/from JSON). All other details about data transport, URLs, redirects etc. would be left to the RP to implement however they please with already existing tools.
@emlun Nice. Our ideas have a lot of similarities. They both embed JSON in an HTML attribute, include a challenge, and send a POST to the RP. They both add one or two new types of HTML element. (Mine adds two new HTML attributes, yours adds an input type="webauthn.get" and "webauthn.create".) Both of our ideas add something to the anchor or form to explicitly identify it as performing user authentication. So the browser does not have to guess which login forms or anchors are used for user authentication. My idea adds a special attribute to the anchor element. With your idea, the browser can test for the existence of either special input element. (type="webauthn.get") Both of our ideas require a new ability from browsers, to intercept the click or submit and perform the WebAuthn ceremony. Then put the results of the ceremony into a POST.
My idea can be changed to behave like yours. My original idea was to send a JSON Web Signature, but that can be changed to send the existing JSON/JS data structure. Similarly, my idea can be modified to include a reference to a server side state, and the "publicKey" and "allowCredentials" properties. If possible, the entire JSON configuration document hosted at the RP (that I suggest) may be omitted. (Although there may be extra reasons to keep it. Allows the full options, it is extensible, and see below.) The end result is that the two ideas can send the exact same POST to the RP. As in your example.
The real differences between our ideas seems to be: (1) I use an anchor. (Although a form can be used too. If necessary.) I am intentionally trying to remove login forms, and make them unnecessary. The browser detects the special attribute, renders the anchor specially. On click, it starts the WebAuthn ceremony, and will send a POST to the endpoint_uri.
(2) My idea has an explicit response to the login request from the RP. This informs the browser of the success/fail and state of the login. I want the browser to be able to keep track of which RP are logged in, so it can provide a "logoutAll" option. It does this by having the RP send a JSON response to the browser, with success or error information. If it can be worked out that the RP can include redirect information in the response, so much the better. The browser can read the "redirect_uri" property etc. from the response, and redirect to the desired page. Or if the redirect_uri is missing, the browser will refresh the existing page by default.
(2b) The response from the RP is caught by the browser, parsed, and the browser updates the Authenticator. With either data from the response, or data stored from the request. (The browser calls navigator.credentials.store)
(3) My idea includes an explicit fallback mechanism. In case the browser does not support the new API. (If the browser does not understand the input type="webauthn.get" HTML element.)
(4) My idea has a discrete "action" property. This allows both "login" and an explicit "logout" request. It also allows the API to be easily extended in the future with other actions. "prove-user-presence", "change-credentials", etc. (i.e. I want to keep the browser informed of what user authentication action is being performed, and what the response is. The browser needs to update the Authenticator with the result, and needs to maintain the login status of the RP.)
(5) My idea does not have an "action":"create-user-login" option in the HTML. I figure that "action":"login" is enough. It can be used for both "get" and "create". The browser or the user can figure out that the Authenticator has no stored credentials for the RP, and create a POST with the "webauthn.create" option.
(6) It may be beneficial to allow the JSON configuration document hosted by the RP as an option. (accessible via HTTPS or FTPS, etc.) My idea is that it may be possible to use this document to perform user authentication for other applications beyond browsers and HTML. Imagine using the same Authenticator to login to other applications, online games (i.e. Steam, World of Warcraft), FTP, etc. As long as those other applications can parse JSON, make a call to the Authenticator, and create the desired transport data. (i.e. the existing JSON/JS data structure or a JSON Web Signature.) I imagine that eventually the Operating System will provide an API to connect to the Authenticator.
@Garnac I have a few problems with your idea:
(1) I use an anchor. (Although a form can be used too. If necessary.) I am intentionally trying to remove login forms, and make them unnecessary.
It should be a form IMO. You are sending info from browser to the server, the usual way to do this without Javascript is
@ignaloidas You make some good points.
I think that a declarative HTML API should not be limited to the current webauthn, (i.e. To a specific version of the Credential Management Level 1, etc.) The API should be made more general than that. It should cover all the general use cases that a browser may need. This is to allow innovation in the future. (i.e. In what ways can the browser benefit the user when doing a user login?)
(1) Login should be done via a form.
I disagree. Requiring an HTML form in order to use webauthn is too limiting. What should the login system of the future look like? (in 10 years, etc.) The entire point of the new login system (webauthn, etc.) is to eliminate the user having to perform manual data entry. So why continue to use an HTML form and prompt people to perform data entry? What is the point of requiring websites to keep using old style HTML login forms, when 90 percent of the time, data entry will not be used. (people will not type anything into the form.)
If you really don't want to have an anchor send a POST, then a new HTML element could be created. (Create a "login" tag, etc. instead of using the "a" tag.) Or the anchor could perform a GET instead of a POST. etc. etc.
As a thought experiment, if it could be done, would it be better to have the "login/logout" button as a standard part of the browser chrome, and not as a visible element in the HTML page at all? (The HTML page would contain hidden attributes for the browser to interact with.) Instead of an anchor tag, how about using a "meta" tag, or a "link" tag in the header. (It might take years to train users how to do things that way, but special browsers or browser extensions could be created that do it. How about accessibility for blind users, etc. etc.)
(2) Webauthn does not have a logout.
OK. So webauthn lacks that use case or feature. I don't think it matters that much.
A declarative HTML API should provide a "logout" option. As using it gives the browser more abilities. It allows the browser to perform pre and post processing (before and after the logout) and figure out the current login state at the RP.
Most RP websites currently have a "logout" option. (As an anchor in the HTML page.) The proposed change is that the RP should use an API that explicitly informs the browser of the RP's intent to perform a logout. (i.e. The browser gets to perform pre and post processing.)
Also, the "action" property allows other actions besides "logout". For example, "prove-user-presence", "change-credentials", etc.
(3) The API should inform the browser of the success/fail of the login request. The resulting RP login state should be maintained by the browser.
(This allows browsers to provide a "logoutAll" button and other features like that.)
OK. Webauthn does not inform the Authenticators if the login action succeeded or not. navigator.credentials.store does not work for PublicKeyCredentials. (It always returns an error.) However, this may not matter that much.
Some sort of response is still required from the RP to the browser. I think that: (1) The response should be caught by the user authentication code in the browser. So that the browser can perform post-processing after a successful login. (Even if webauthn does not do post-processing, the browser needs to provide the hook for it. For browser UI updating, browser extensions, etc.) (2) The browser should be able to tell if the response signals a login success or failure. The browser should keep track of the login status at the RP. (3) The response can also contain a redirect_uri. (Or the response can be a regular HTTP redirect response, as long as the other two criteria are met.)
Webauthn may not require a success/fail response or post-processing, but these traits are desirable for other use cases. The browser may want to do post-processing for some other reason (accessibility, visual cues in browser chrome, etc.), or other protocols may require post-processing. (Using a "password" Credential Management Level 1. etc. Also, by using new properties in the JSON, the RP can declare support for some other standard, and the browser can choose to use that standard.)
The idea is to give browsers the information about the RP login status, so as to allow browsers to innovate and automate as much as possible and provide more features for their users. Browsers are user agents, they act for the benefit of the user. They should be provided basic login information so as to allow them to do more for their user. (Such as to remember the login/logout status of RPs.)
For example, it would probably be a useful thing for browsers to have an easy way for the user to "logout from the browser". To close (or at least hide) all the current tabs that are logged in and may display sensitive information. Closing the browser will work, but should that be the only option? Would it not be convenient for the user to be able to close (or temporarily hide) all sensitive information in their browser in some situations? (If the user goes to the bathroom, if the browser is not used for an hour, etc.) Advanced users could use a browser setting to choose what they want to happen, etc.
I think it would a bad thing to not provide browsers with the explicit login status of the RP. This would limit browsers, so that when they innovate, they have to guess as to the RP login status.
(4) Using the JSON document for other applications.
Sounds good, a separate JSON document may not be needed for these cases at all. OK.
As a thought, if the URL for JSON document was moved to a "meta" or "link" element in the header, maybe that would allow HTTP/2 to deliver it along with all the other documents as part of the page load. That would cut out the additional HTTP request.
@Garnac What you're proposing is a much more ambitious vision for a standard web-wide sessions framework, which is way out of scope for the current WebAuthn working group. Even if limited to just emulating the JS API with declarative HTML, a completely new interface won't even be seriously considered by the WG. If we can make it a minimal addition that reuses as much as possible from the JS API, then maybe.
In my opinion, the webauthn working group has betrayed its mission in the pursuit of shiny things if it does not build an option which doesn't require JavaScript.
I agree with @emlun. Crafting a declarative HTML-based means to utilize the webauthn/fido protocol is "possible" and might be something to do down the road after we've gathered further deployment experience. We ought to keep this issue "around" as food for thought, perhaps in a "Futures" milestone.
@ignaloidas I don't see how would you make FIDO2 work without JS. It is requires four steps:
So it seems to be an overkill to create one declarative HTML API for something like that.
@herrjemand To break up:
Noting that browser already kind of does 2 and 3 by itself(it is abstracted under JS API) there isn't any real stoppers for this.
mentioned on 27-Nov-2019 call: @jcjones noted that perhaps the "pluggable authn mechs" being discussed in the QUIC IETF WG are potentially a means to ultimately address the anticipated use cases here?
@jcjones also noted that browser engines have been re-architected of late (eg gecko) such that accomodating functionality like is proposed here in the "DOM layer" would be difficult.
I think that a QUIC "pluggable authn" is along the same lines as what I was thinking about.
@equalsJeffH can you provide a link or some more information on the "pluggable authn mechs" discussion?
If the authentication happens in the QUIC or HTTP/3 protocol, then that means that an HTML form is NOT used to do authentication. The authentication method is decoupled from the user's interactions with the HTML page. (The browser does not have to wait for the user to click an anchor or submit a form.) The browser may still interact with the user, for example to ask the user which credential or login to use with the RP, but this interaction is between the browser and the user, it is not a custom interaction between the RP and the user. (Performed by the RP's web page, including custom HTML and javascript.)
This is great. It gives the browser the tools it needs to innovate.
For browsers and websites that do not support xxx new technology (QUIC, etc.), there should be an easy way to shoehorn the user authentication into existing technologies. (i.e. into HTTP or HTML, etc.)
The HTML changes to allow for user authentication should NOT require the use of an HTML form, nor an HTML anchor. Although a form or anchor can ALSO be included in the HTML. For cases where an RP wants to provide custom actions (i.e. a redirect uri, etc.) or an on-page login interface for users. It allows an RP to provide users a more familiar interface. (i.e. users can login by interacting with the HTML page.)
The "pluggable" user authentication settings data (i.e. the webauthn settings) should be put into a special non-visible element in the HTML page header. Probably a meta tag or a link tag. (Alternately, the webauthn settings can be put in a separate JSON file on the server, and the meta or link tag can reference that file. In the href attribute, etc.) This allows the browser to retrieve the webauthn settings and perform webauthn (or some other future protocol) without requiring javascript, or that the user interact with the RP through the HTML page, etc.
Each user authentication method would need to define its own settings in the HTML. (A "pluggable" user authentication system needs to support multiple user authentication methods. i.e. other options besides "webauthn". The methods could be called a "protocol", or an "RP service", etc.)
Example:
<link rel="capis-act" href="/user_auth_config.json" capis-config=' "rpInfo": { "displayName": "ACME Corporation", "siteLogo": "/images/logo.png" }, "serviceList": [ { "protocol":"webauthn", "endpoint": {"http_method":"POST", "uri_address":"/exampleapp/webauthn/finish_assertion", "parameterList": [{"name":"user_auth_param", "type":"JSON"}], "assertion_format": "assertion format1", "creation_format": "creation format1" }, "customData": { "exampleapp_request_id":"3a74f2b4-cff0-4f50-8076-2f5532a0d6f3" }, "challenge": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "assertionOptions": { "propertyName": "assertionInfo", "publicKey": { "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "allowCredentials": [{ "type": "public-key", "id": "Pzy_hxuR849qZbLAE8Vr4U3KPiIN6W10ssJOR55BMZQ", "transports": ["usb", "nfc", "ble", "lighning", "internal"] }], "userVerification": "preferred", "timeout": 120000, "extensions": {"loc": false } } }, "creationOptions": { "propertyName": "creationInfo", "publicKey": { "challenge_old": "WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs", "rp_old": {"name": "ACME Corporation"}, "user": {"id": "bYq3-Uk0J3xMt", "name": "alex.p.mueller@example.com", "displayName": "Alex P. Mueller"}, "pubKeyCredParams": [{ "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -257 }], "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "requireResidentKey": true, "userVerification": "preferred" }, "timeout": 360000, "attestation": "none", "excludeCredentials": [], "extensions": {"loc": false } } } } ], "nonceIssueTime":1533064012, "nonce":"WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs" '/>
Later in the HTML.
<a href="/fallback/login.html" rel="capis-act" capis-config=' "serviceAction":"webauthn/login", "redirect_uri":"/page2.html" '>Login Here</a>
Notes:
Example POST sent to the RP. "assertionInfo" may be sent as a separate parameter.
user_auth_json={ "serviceAction": "webauthn.get", "customData":{"exampleapp_request_id":"3a74f2b4-cff0-4f50-8076-2f5532a0d6f3"}, "assertionInfo": { "credentials": [{ // Allow multiple credentials, for a future extension? "id":"K3xM080fiCDCkv412SdQ6--982rRf9i6NtDY0Jkv-AJZaCH9-MNs-ijV2y2OXKbxjzD3rXR05rmGF_jGQnXyPQ", "response":{ "authenticatorData":"xGzvgq0bVGR3WR0Aiwh1nsPm0uy085R0v+ppaZJdA7cBAAAANQ==", "clientDataJSON":"eyJjaGFsbGVuZ2UiOiJwa...", "signature":"MEUCIHw..." }, }], "clientExtensionResults":{}, "transports":["usb"] } }
I doubt browsers want to revive the <keygen>
element, but it addressed a lot of design decisions brought up in this thread. A strawman using only already-standardized HTML could look like:
<form method="post" action="/exampleapp/webauthn/finish_assertion">
<input name="user" autocomplete="username">
<keygen name="webauthn"
required
keytype="webauthn-public"
keyparams="alg -7"
challenge="WwdPrNbUcI1034qZ9LYks32ZPezDLZSieFI9f6NaZZs">
<!-- fallback for non-implementing browsers goes here -->
</keygen>
<input type="hidden" autocomplete="webauthn-userid">
<input type="hidden" name="exampleapp_request_id" value="3a74f2b4-cff0-4f50-8076-2f5532a0d6f3">
</form>
The required
attribute is reused for the WebAuthN API’s userVerification: 'required'
. hidden
could be used for userVerification: 'discouraged'
, and 'preferred'
would be the default.
Multiple credentials as mentioned by @Garnac could be implemented with multiple elements.
Of course, this strawman lacks a lot of the discussed functionality, but I think it’s worth learning from the Web of yesteryear for how one might implement webauthn without JavaScript.
@equalsJeffH where could I find more info on it?
I don't think QUIC specific technology would address all the use cases. Namely Tor wouldn't be able to use it, as it doesn't support UDP that QUIC uses. Personally I don't think that authentication should be coupled with transport mechanisms, as that would prevent using it via alternative methods of transport, since not all of them might not be available. E.g. UDP is currently quite often blocked in networks. Now QUIC might change this, but it would take a while for change to happen. Thus a bizzarre situation might happen where both a server and the browser support an auth mechanism, but can't use it because something in between doesn't.
Hi folks, I work on HTTP/3 and QUIC for Chrome. I'm jumping in to answer questions about QUIC authentication.
There hasn't been much discussion of "pluggable authn mechs" in the IETF QUIC working group. More specifically, the IETF QUIC WG is currently very focused on shipping HTTP/3 with TLS 1.3. That said, the QUIC transport and authentication documents were intentionally split so that one could "plug" a new authentication/key exchange scheme as a replacement for TLS. The one example I know of is nQUIC, but even that is a research project that was never deployed in production.
Another area of research that is worth noting is the integration of a PAKEs with TLS. One prominent candidate is OPAQUE: see OPAQUE-TLS. The idea there is to allow proof of password ownership to be transmitted at the TLS layer without actually transmitting the password. This also provides bindings between the exchange and the TLS connection, making man-in-the-middle proxies' lives harder.
So if Webauthn were to pursue authentication at the transport layer, then I would strongly recommend making this at the TLS layer instead of QUIC, because it would allow working over networks that block UDP (or server deployments that aren't ready for QUIC yet), and also because it would not require reinventing a new authentication / key exchange scheme, which is fraught with peril.
All that said, I'm not sure moving webauthn to the transport is necessarily the right idea. Almost all Web/HTTP authentication has been historically performed at the application layer; and, if we were to change it, it should probably involve a bigger effort that also tries to do this for passwords. But that might take copious amounts of time and might derail the conversation about Javascript...
I surprised that WebAuthn has ONLY javascript API, without a pure html version. But I supsect that this is because a lot of aspects is required to be defined, especially at scope like "developers taste". For me, this can be done with using these:
What I wanted to avoid or make optional, is having JSON blobs at attribute values, and allow prerendering old dynamic stuff, like challenge for registration forms.
using unsupported type for input will allow to fallback to js scenario in case if html-only form auth does not supported.
In the "authentication" input attribute "scheme" can be various, in case if e. g. scheme like webauthn/2.0 will emerge, or even "basic" if it is suitable for context/project.
Any no-js solution for advanced authentication require following - work with W3C to get consensus on how represent complicated auth schemes, like webauthn in html.
Another question - how to guarantee for example, that anything that has been considered as non-public, downloaded since registration/authentication flow successfully completed, would be authenticated with this public key auth? Like if user requests a protected page, server returns 401, and what server must say in challenge details to kick in webauthn flow? Should server send that fancy JSON as body for 401 response?
Ultmate flexibity is not just avoiding JS, but option to not having even html at all, and use the public key created in webauthn flow to authenticate all or "protected" requests within session regardless of usage of JS or even html. Webauthn concept actually allow to introduce the ability to register user without JS and html in general but this may be beyond the scope of discussion.
prerendering old dynamic stuff, like challenge for registration forms.
You should never reuse a challenge
, it should be uniquely generated for each registration/authentication ceremony. See §13.4.1. Cryptographic Challenges
(and pardon me if I misinterpreted what you meant).
prerendering old dynamic stuff, like challenge for registration forms.
You should never reuse a
challenge
, it should be uniquely generated for each registration/authentication ceremony. See §13.4.1. Cryptographic Challenges (and pardon me if I misinterpreted what you meant).
Nice catch!
The options object can have not "challenge", but "challengeSource (url), expected to return e. g. application/octet-stream
with raw bytes for challenge, and can have some restrictions like request CORS restrictions must apply, and require some rule for presense of referrer
within same domain..
What do you think about "webauthn without html" scenario, especially
Like if user requests a protected page, server returns 401, and what server must say in challenge details to kick in webauthn flow? Should server send that fancy JSON need to kick webauth login flow as body for 401 response?
Of course this will require sending public key generated from credentials to work in full "no-html" scenario after login via webauthn was made, for all requests within related origin.... Which can replace cookies used for keeping login sessions today....
I feel like this should be added to L3 charter. I believe this could increase adoption of webauthn and maybe even allow for smaller, simpler browsers to implement this subset quickly.
Bump. This should not be sidelined, it needs to be a major priority for webauthn.
A big issue is that currently the webauthn spec practically forces client side transform of requests/responses due to the usage of uint8arrays which can't be encoded to json. When requested to have these as base64 instead, it was denied. So I think that would be "step 1" is remove the need for client side transform of any webauthn request. See https://github.com/w3c/webauthn/issues/1619
Transport issues such as base64 vs binary field values are not nearly as interesting as the user experience and abstract protocol.
Are people expecting this to work as a HTTP Authentication Header protocol? As new attributes embedded into an HTML form? Or in some other manner entirely? What is the expected user experience? How does that experience degrade if the user e.g. cancels or taps an authenticator that does not meet server policy?
In all honesty, we would be better off representing the whole response as base64url-encoded CBOR if we were talking about ease-of-transmission to the relying party server. Use of JSON is optional for constructing requests and verifying responses, use of CBOR and COSE and the processing of other binary-structured messages are not.
Was this issue resolved? I know the standardization process can be a lot of work but this is extremely important. Thank you
@AadaEa No. There is work to allow a "transformation" helper in navigator.creds, but you will always need javascript. See https://github.com/w3c/webauthn/pull/1703
Could there be a potential compromise here in initially focusing the non-JavaScript design on the use-cases that motivated non-modal UI (#1545)?
While I do understand that several participants in this issue would prefer to have a "UI-free", webauthn-only approach, and that this would likely not satisfy that goal, for existing systems that already have username/password support and which aim to add webauthn support it could perhaps be a good start to support that without JavaScript, for easier retrofitting into existing designs built around HTML forms.
The conditionally-mediated UI already relies in part on declarative, HTML-only features: the autocomplete="webauthn"
annotation to help the user-agent associate the webauthn flow with the existing login fields via the password autofill UI.
This must currently be accompanied by a call to navigator.credentials.get
to activate the behavior. Could a new HTML element or extension of existing HTML element be a declarative substitute for navigator.credentials.get
to activate the conditional mediation behavior without JavaScript, placing the result into a form field and immediately submitting the form?
I note that this simplification would limit the JS-free mode only to logging in with existing credentials and not to registering new credentials. This means that, as with systems relying on the JavaScript-based conditional-mediation approach, it would still be presumably necessary to build a separate registration flow for webauthn, alongside the username/password one, and it would admittedly still need JavaScript unless there were a similar non-modal registration flow that could also have a similar HTML variant.
I would like to see a TLS and or HTTP layer protocol for Webauthn, so that the browser provides all of the UI and the website provides none, similar to TLS client certs or HTTP basic auth.
When the user requests a page that requires (or is enhanced by) login, the website responds saying authorization required (or suggested), then the browser pops up a "Login to this site?" similar to webcam/etc prompts, then the user clicks login, then the browser sends the credentials and the website responds with success/failure. For future requests, the browser sends credentials again, until the user clicks the logout UI.
Perhaps there should also be similar mechanisms for adding new keys and removing old ones, for both user initiated replacement and browser or website initiated replacement. Registering/removing accounts might also be nice to have too.
2022-11-16 WG call: There is some interest in this from Safari and Mozilla, but still low priority. This likely won't happen in the short term, but perhaps at some point. No promises yet.
Please do push for inclusion of webauthn in the HTML standard and make it a mandatory part of all browsers. javascript is a security risk.
I think it would be better at the HTTP or TLS layer, so that API clients and similar non-browser clients can also use WebAuthn.
-- bye, pabs
I think it would be better at the HTTP or TLS layer, so that API clients and similar non-browser clients can also use WebAuthn.
Yes, it sounds reasonable. But from HTML point of view it may be require general elements or attributes. To my taste, at HTML changes must be more declarative, like:
<form method="authenticate" scheme="webauth/1.0" id="login_form">
<configuration type="challenge" scheme="webauthn/1.0" src="/api/auth/webauthn/challenge" />
<configuration type="webauthn/settings:json" scheme="webauthn/1.0>
{
/* some JSON with webauthn options that acceptable to be rendered at any webauth action */
{
</configuration>
<input type="username" id="username_box" />
<input type="credential/select" scheme="webauthn/1.0" form="login_form" />
</form>
Surely, this declarative approach require some specialized usage of well-known headers like Authorization
to make sure they can be used by APIs and their native clients like mobile devices and other user cases. Even browser will have to work like that if that html-only approach will be used:
Authorization: WebAuthn v=1.0;name=value;name2=value2.....
I would rather the browser providing all of the user interface for the login flow, that way every single website doesn't have to reinvent the wheel and users get much more consistency. Something like the webcam permissions popups. The browser would show "This website wants you to login! Login / Ignore" and users would click through that. When the user doesn't have a passkey for the site, the UI could say register instead of Login. There could be a browser UI indicator that you are logged in and a way to logout or request deletion of your account.
-- bye, pabs
I would rather the browser providing all of the user interface for the login flow, that way every single website doesn't have to reinvent the wheel and users get much more consistency. Something like the webcam permissions popups. The browser would show "This website wants you to login! Login / Ignore" and users would click through that. When the user doesn't have a passkey for the site, the UI could say register instead of Login. There could be a browser UI indicator that you are logged in and a way to logout or request deletion of your account.
I understand your point, but let me remind few points why websites would prefer style-able HTML over something fully controllable by browser: 1) Today all auth UI shown by browser looks so "unfinished" and "unbranded", that between using something like Basic auth over https, or even certificate based auth with certificate from auth card, and something with styled HTML UI with username and password boxes, sites automatically pick the later in favor of UI branding consistency. 2) there is no a clear way to logout from such authentications with browser UI, e. g. Basic auth over https. 3) Each website have own understanding of various aspects. One site have separated username and email, other websites use email as username, third use email OR username (you can type both). One website offer remember username only (remember me), others offer to remember user sign in for a while (keep me signed in), others do not show these checkboxes and assume users want be signed in for a while by default. Site authors want anything within website look within same style, as other UI. This is one of corner stones of success of Stripe Elements which are literally provide a way to visually "embed" sensitive data inputs into website with proper styling, while still the CC numbers and such not exposed to website JavaScript or not sent in plain text to website backend.
I would rather the browser providing all of the user interface for the login flow, that way every single website doesn't have to reinvent the wheel and users get much more consistency. [...]
I understand your point, but let me remind few points why websites would prefer style-able HTML over something fully controllable by browser:
- Today all auth UI shown by browser looks so "unfinished" and "unbranded", that between using something like Basic auth over https, or even certificate based auth with certificate from auth card, and something with styled HTML UI with username and password boxes, sites automatically pick the later in favor of UI branding consistency.
I think a few things get mixed up here. Basic auth and client certificates are something which the browser must show as the connection to the website requires this. So for these things there is a fairly barebones UI and I agree with you that the developer has little control over what is shown and how it is used. Therefore these mechanism are also mostly used in communication between servers or scenarios where this is automatically resolved like kerberos tickets.
For WebAuthn, however, I think that letting the browser provide the UI is in the best interest of the devs. For one, they still control most aspects of the auth flow, like when it is initiated and can provide instructions beforehand. Second, there are so many different methods how WebAuthn can be achieved that is is infeasible to provide a UI for each way. There are hardware keys, fingerprint, face ID, Windows Hello, nearby bluetooth devices etc. Even providing the dev with information about which methods the platform supports could leak private information.
Furthermore on many platforms having the UI be provided by the browsers leads to more consistency. E.g. windows has a consistent Windows Hello prompt to my knowledge and all mobile devices have their own UI for biometrics which are consistent across the platform so we don't want to intercept them here. Also not every browser provided UI is bad. Think for example about file selectors, or date selectors on mobiles. Most versions to implement them inside HTML would result in a catastrophical leak of information or result in worse UX.
- there is no a clear way to logout from such authentications with browser UI, e. g. Basic auth over https.
This is true for basic auth and TLS client certificates but not for WebAuthn. Here the session is completely in control of the website. Reading this I think you might also be referring to the comment from @pabs3 before the one you quoted in which case I am definitely on your side. So I will also respond to that:
I think it would be better at the HTTP or TLS layer, so that API clients and similar non-browser clients can also use WebAuthn.
I do not think WebAuthn belongs in the HTTP or TLS layer. We already have mechanisms to authenticate there but these are mostly used for server to server communications. You already said that this mostly applies to server-to-server communication. A situation in which many of the possible WebAuthn advantages over traditional alternatives do not exist or cannot be utilized. You would not want to have your backend service require any kind of popup to request you biometrics or a hardware key. Instead you would usually use a strong token. If one requires an asymmetric auth flow one can still use TLS client certificates. And should those be too cumbersome you can still set up a mechanism similar to WebAuthn. Still, you are probably better of to just not do it.
- Each website have own understanding of various aspects. One site have separated username and email, other websites use email as username, third use email OR username (you can type both). One website offer remember username only (remember me), others offer to remember user sign in for a while (keep me signed in), others do not show these checkboxes and assume users want be signed in for a while by default. Site authors want anything within website look within same style, as other UI. This is one of corner stones of success of Stripe Elements which are literally provide a way to visually "embed" sensitive data inputs into website with proper styling, while still the CC numbers and such not exposed to website JavaScript or not sent in plain text to website backend.
I do not see why this would be incompatible with the browser providing WebAuthn UI. They can still control the input of username and other information and would simply invoke the browser UI upon submitting the information. Also there is another issue about providing an intent like "Register", "Signin", "Authenticate transaction" or others which I really like (#1823).
Regardless: This issue is about performing WebAuthn without JS and not about the UI provided by browsers which are independent issues.
The issues with basic auth and TLS client certs are browser UI issues that should be fixed by browser vendors. Web devs need to put pressure on those vendors to have better support for browser-native auth flows. I have filed a bug with Mozilla about that for TLS client certs a while ago, but there hasn't been any movement, not sure how to change that, hopefully someone here has some contacts. The current UI for those are inexcusably bad and it is incredible how long this has persisted.
The basic auth and TLS client cert protocols definitely support logout (just don't send the basic auth HTTP header or TLS auth extensions), so lack of logout is again a browser UI issue that should get fixed.
I was thinking that after a user logs in with the HTTP/TLS based WebAuthn protocol, the website would map their WebAuthn credential to the appropriate username/etc, or if none exists on the service, then the website would return the account registration flow including creating a user and registering their WebAuthn credential to that user. The website itself wouldn't have any logout functionality, that would be provided solely by the browser UI.
WebAuthn definitely belongs in HTTP or TLS not anywhere else. Using WebAuthn on a server would ensure that the API access credential can never get duplicated if the server ever gets compromised, when the credential is just using a USB WebAuthn device. This is highly desirable in some environments. HTTP tokens can leak from the remote service or get duplicated during hacks or be sent in the clear etc.
I still think the best way to do WebAuthn without JavaScript is to add it to both the HTTP and TLS layers. The HTTP layer so web app authors can workaround web servers they don't control. The TLS layer so that auth can be checked on TLS forwarders/load balancers and web servers.
-- bye, pabs
The issues with basic auth and TLS client certs are browser UI issues [...]
The basic auth and TLS client cert protocols definitely support logout (just don't send the basic auth HTTP header or TLS auth extensions), so lack of logout is again a browser UI issue that should get fixed.
I read somewhere that Edge allows TLS logout by clicking on the padlock icon in the URL. This is not discoverable, but more or less any place in the native Browser UI would be hard to discover. It is significantly easier if the website can place it in intuitive locations. And the browser cannot do that because it shall not inject something into the web page. One might think to make it a large button somewhere in the browser UI but that won't work for mobile browsers.
So the logout button should be under control of the website. This is also important for other scenarios e.g. banking applications which want to log the user out after some inactivity. Similarly you want to invalidate all sessions after e.g. a password change. There once was window.crypto.logout()
though it was removed.
But this is a completely different topic independent from the one discussed here. Even if the UI provided by the browser was ideal there would still be reasons why this might be better off in the HTML standard and not HTTP or TLS...
I was thinking that after a user logs in with the HTTP/TLS based WebAuthn protocol, the website would map their WebAuthn credential to the appropriate username/etc, or if none exists on the service, then the website would return the account registration flow including creating a user and registering their WebAuthn credential to that user. The website itself wouldn't have any logout functionality, that would be provided solely by the browser UI.
For all but very simple scenarios, I do not think HTTP Auth and TLS client-certs are not flexible enough even though I would like them to be. Login might be the simplest case but even then there are many inflexibilities: E.g. services which also allow login through OpenID Connect providers. You cannot give them a choice to choose if you require authentication before the website is loaded. But for registration this becomes even worse: No way to convey username guidelines, no way to request other information like birthdate, no way to provide legal disclaimers. Obviously this could all be embedded by some options but that is waaay out of scope of WebAuthn. This flexibility can only be provided if the website itself can provide the UI of the login/registration process.
Also in case of HTTP which is stateless what do you want to transmit after the first request? You would have to request and sign a new challenge each time and asymmetric crypto is slow. It gets even worse if you want to use post-quantum secure algos. And implementing this on the TLS layer seems way to complex given that client-certs already cover a lot of the same grounds.
If you want support for WebAuthn in these other layers you should create corresponding issues for that but I doubt they will get much support. WebAuthn is simply too complex for and incompatible with these layers.
Even if WebAuthn support were to be added to these lower layers, it would still be part of the HTML/JS stack. And for this case we STILL want a way to use it in noscript mode. Just because it offers so much more control to the website authors.
Regardless: This issue is about performing WebAuthn without JS and not about the UI provided by browsers which are independent issues.
I not fully agree here. As I mentioned earlier, lack of professionally-looking UI in browser is one of key reasons of why website provides own UIs for that.
Also, one of strange aspects of in-browser UI at least of desktop is usage of uncomfortable popups even if some full-page UI can be shown instead with some domain-specific details like icon, title and description and other details.
But I totally agree, that implementing this approach of common UI of "select your account or begin registration of new one", and so on, is related to browser capabilities, not specific to WebAuthn. Still, I insist, that work over a convention of how to unify and standardize auth flows like login
, register
authorize/payment
(assuming it may be something else that can be authorized, like digital document signatures, across all these schemes of auth, will help to adopt schemes like WebAuthn.\
The basic auth and TLS client cert protocols definitely support logout (just don't send the basic auth HTTP header or TLS auth extensions), so lack of logout is again a browser UI issue that should get fixed.
To my understanding, it will be very helpful for website developers is to add something like
<button type="logout">Log Out</button>
To make sure logout procedure explicit and button for that can be placed in a way website design demands to place it - at site header, in some user account menu, etc. But then it would require like
<div demand="signed-in|guest">
....content for specific case...
</div>
Well, it all shows, that completely without javascript, declarative components necessary or attributes and values to existing, to make sure all common tasks related to sign in, sign out, sign up, and hiding some element of UI. Some things may require new CSS selectors like auth:guest
or auth:user
or even auth:schema(schema_name)
may be necessary.
It is not a "click here" scope, but without all these details webauth will not be freed from JavaScript dependency.
Some final thoughts:
The webcam/etc permission decisions are discoverable, so there is no reason the same sort of browser UI couldn't be used for WebAuthn, Basic Auth and TLS client cert logout.
The problem with the web page logout button is that it looks different on every website. The user also cannot be certain what it does. It could still track the user via cookies even though they logged out. Some of them may use GET requests instead of POST, which means malicious websites could log you out of some other website.
The website would still be the initiator of WebAuthn login events, so the choice of login provider would still be possible.
For registration you would convey the guidelines in the registration form that appears after you authenticate via WebAuthen but without yet having a user account. The registration data would be transmitted by normal HTTP form submission separate to the WebAuthn credential and the registration submission would tie the username to the credential.
In my proposal, there would be no sessions and hopefully no cookies, just "logged in as foo" or "not logged in" states.
I don't know enough about WebAuthn to answer about the challenges, but TLS client certs don't have slowness, so I doubt WebAuthn will either.
The advantage of WebAuthn over TLS client certs is there aren't any
common hardware tokens for certs and browser vendors prefer WebAuthn so
they might eventually just remove TLS client certs entirely, since they
have already removed some related features like
Anyways, it is clear my HTTP/TLS proposal isn't going to be accepted nor well liked among web developers, so I'll stop discussing it here.
-- bye, pabs
By comparison, you could invoke an Information Card Selector using either an object tag or an XHTML element. These are specified at http://docs.oasis-open.org/imi/identity/v1.0/os/identity-1.0-spec-os.html#_Toc229451904.
@MasterKale any way I can help out with this? I really want to get this problem solved 💪
Hey @tcannonfodder, I've been on PTO for the last two weeks and won't get back into work mode till Monday. I'll review the current state of this issue next week to refresh my memory and more meaningfully respond to you ✌️
Not sure if it's particularly helpful for this issue. But I managed to get Webauthn working without XHR; but using a multipart
form submission instead. This way I don't need to pull in any libraries for serializing binary data to json.
It at least kind of keeps the semantic html. And whatever non-js version of Webauthn should probably have this as behaviour in some way or another.
<!doctype html>
<form action="login" data-challenge="{{ .Challenge }}" method="post" enctype="multipart/form-data">
<input type="text" name="username" autocomplete="username webauthn">
<input type="hidden" name="type">
<input type="hidden" name="id">
<input type="file" name="clientDataJSON" accept="application/json" hidden>
<input type="file" name="authenticatorData" accept="application/octet-stream" hidden>
<input type="submit" value="Submit">
</form>
<script type="module">
function submitCredential(credential, form) {
form.elements.namedItem('type').value = credential.type;
form.elements.namedItem('id').value = credential.id;
const clientDataJSON = new DataTransfer();
clientDataJSON.items.add(new File([response.clientDataJSON], 'clientDataJSON'), { type: 'application/json' });
form.elements.namedItem("clientDataJSON").files = clientDataJSON.files;
if (response instanceof AuthenticatorAttestationResponse) {
const attestationObject = new DataTransfer();
attestationObject.items.add(new File([response.attestationObject], 'attestationObject'), { type: 'application/cbor' });
form.elements.namedItem("attestationObject").files = attestationObject.files;
}
if (response instanceof AuthenticatorAssertionResponse) {
const authenticatorData = new DataTransfer();
authenticatorData.items.add(new File([response.authenticatorData], 'authenticatorData'), { type: 'application/octet-stream' });
form.elements.namedItem("authenticatorData").files = authenticatorData.files;
}
form.submit();
}
const login = document.forms.namedItem('login');
const challengeLogin = Uint8Array.from(atob(login.dataset['challenge']), c => c.charCodeAt(0));
login.onsubmit = async (event) => {
event.preventDefault();
const credential = await navigator.credentials.get({
publicKey: { challenge: challengeLogin, userVerification: 'required', allowCredentials: [] },
});
submitCredential(credential, form);
};
if (PublicKeyCredential.isConditionalMediationAvailable && await PublicKeyCredential.isConditionalMediationAvailable()) {
const credential = await navigator.credentials.get({
mediation: 'conditional',
publicKey: { challenge, userVerification: 'required', allowCredentials: [] },
});
submitCredential(credential, login);
}
</script>
I'm excited to prototype an implementation over the coming weeks as a strawman!
I'll be adding it to: https://git.argonaut-constellation.org/~alcinnz/bureaucromancy
There's significant architectural differences between how I implement webforms vs most other browsers, but that should help progress this issue!
Note: there is an initiative to add FIDO to the TLS stack as an alternative to TLS client authentication. Wouldn't that address the same use case? It wouldn't need JS.
@rlin1 IIUC, I don't think it would address my typical use-case, which wants to have some pages accessible unauthenticated and others authenticated, and some available both ways. Client authentication would need to happen globally or perhaps per-resource, right, and be a total block to the resource?
@rlin1 IIUC, I don't think it would address my typical use-case, which wants to have some pages accessible unauthenticated and others authenticated, and some available both ways. Client authentication would need to happen globally or perhaps per-resource, right, and be a total block to the resource?
you could require it based on the URL - and still provide access to the same resource if you wanted.
To be clear, when I think of "use WebAuthn without JavaScript" I think of "enabling the use of a <form>
and a <button type="submit">
to initiate WebAuthn ceremonies." It's the what of the contents of that <form>
that is the most compelling line of thought for me here. Why can't calling WebAuthn be as simple as it is to create a standard HTML form to collect username+password?
To be clear, when I think of "use WebAuthn without JavaScript" I think of "enabling the use of a
<form>
and a<button type="submit">
to initiate WebAuthn ceremonies." It's the what of the contents of that<form>
that is the most compelling line of thought for me here. Why can't calling WebAuthn be as simple as it is to create a standard HTML form to collect username+password?
1000% co-sign; this should be the way it is! 💪
Adding a way to use webauthn without Javascript should be considered. For example most Tor users have Javascript always disabled, and having a way to use webauthn without JS would allow use of 2FA in Tor. Also, some projects want to be fully usable without the use of JS, and having a way to use 2FA would open the way for those projects too. I'm not too sure how it should be implemented though. Maybe
<input type="webauthn">
?