;
125 |
126 | where each cert and the identity assertion are base64url-encoded data structures, as defined above.
127 |
128 | Web-Site Signin Flow
129 | --
130 |
131 | This section is informative.
132 |
133 | Consider a web site, http://example.com, receiving a visit
134 | from a user. This web site wishes to obtain the user's verified email
135 | address using BrowserID. The user in question, for the purposes of
136 | this description, is Alice. Alice owns two email addresses,
137 | alice@homedomain and alice@workdomain.
138 |
139 | * example.com presents a login button with a JavaScript click handler.
140 | * when Alice clicks the login button, example.com's click handler invokes:
141 |
142 | navigator.id.get(gotAssertion);
143 |
144 | where gotAssertion is a callback function.
145 | * Alice is presented with a user-agent dialog that lets her select which email to present to example.com.
146 | * If Alice chooses to cancel the transaction, gotAssertion is invoked with a null argument.
147 | * If Alice chooses to authenticate using one of her email addresses, gotAssertion is invoked with a Backed Identity Assertion.
148 | * example.com should take this assertion and pass it back to its server. This can be accomplished with an AJAX request. For example, using jQuery:
149 |
150 |
151 | function gotAssertion(assertion) {
152 | $.post("/verifyAssertion", {assertion: assertion}, afterVerifyAssertion);
153 | }
154 |
155 |
156 | This assertion is a Backed Identity Assertion, as defined above. We call it assertion here for simplicity, since the Relying Party typically need only pass this assertion to a verifier service without worrying about the specific semantics of the assertion string.
157 |
158 | Identity Provisioning Flow
159 | --
160 |
161 | Consider Alice, a user of EyeDee.me, with email address alice@eyedee.me. Alice wishes to use her alice@eyedee.me identity to log into web sites that support the BrowserID protocol:
162 |
163 | * Alice visits example.com and clicks "login."
164 | * In the BrowserID interface, Alice types her email address alice@eyedee.me.
165 | * The user-agent checks https://eyedee.me/.well-known/browserid and determines that eyedee.me supports BrowserID. From this configuration file it determines the provisioning and authentication URLs.
166 | * The user-agent loads, in an invisible IFRAME, the provisioning URL https://eyedee.me/browserid/provision.html, delivering to that URL any cookies that have previously been set and making available to that page's JavaScript any localStorage that corresponds to the eyedee.me origin.
167 | * The provisioning URL's script determines if Alice is properly authenticated and, if so, triggers key generation within the user agent, obtains the public key, signs it, and registers the resulting certificate with the user agent:
168 |
169 |
170 | // get parameters of provisioning
171 | navigator.id.beginProvisioning(function(email, cert_duration) {
172 |
173 | // ... check if the current user is authenticated as 'email' ...
174 | if (notAuthenticated()) {
175 | navigator.id.raiseProvisioningFailure("user isn't authenticated");
176 | return;
177 | }
178 |
179 | // request a keypair be generated by browserid and get the public key
180 | navigator.id.genKeyPair(function(pubkey) {
181 |
182 | // ... interact with the server to sign the public key and get
183 | // a certificate ...
184 | someServerInteraction(function(cert){
185 | // pass the certificate back to BrowserID and complete the
186 | // provisioining process
187 | navigator.id.registerCertificate(cert);
188 | });
189 | });
190 | });
191 |
192 |
193 | * If Alice is not properly authenticated, the user agent loads the authentication URL https://eyedee.me/browserid/authenticate.html in a dialog interface, where Alice can then proceed to log into EyeDee.me using whatever flow/method EyeDee.me wishes.
194 |
195 |
196 | // set up UI
197 | navigator.id.beginAuthentication(function(email) {
198 | // update UI to display the email address
199 | });
200 |
201 | function onAuthentication() {
202 | // check if the user authenticated successfully, if not, tell them
203 | // it's a bad password. otherwise..
204 | navigator.id.completeAuthentication();
205 | }
206 |
207 | function onCancel() {
208 | navigator.id.cancelAuthentication();
209 | }
210 |
211 |
212 | Once this is successfully completed, the user-agent returns to the BrowserID user-interface, and attempts to load the provisioning URL as in the previous step.
213 | * Once a certificate for alice@eyedee.me is installed, the user-agent completes the login to example.com by creating an assertion and delivering it to example.com as in the Main Protocol Flow above.
214 |
215 | By the end of this flow, Alice has obtained, within her user-agent, a certificate for her email address issued directly by her email address's domain.
216 |
217 | User-Agent Compliance
218 | --
219 | This section is normative.
220 |
221 | The User-Agent plays an important role in BrowserID support. Here, we define, normatively, the API that user agents MUST implement, including specific behaviors in response to these API calls. Relying Parties and Identity Providers can safely skip this section.
222 |
223 | A compliant BrowserID User-Agent must implement the navigator.id object, which serves both for issuing assertions and exposing a provisioning flow to identity providers.
224 |
225 | ### Issuing Assertions ###
226 |
227 | The User Agent MUST offer the following API call:
228 |
229 | navigator.id.get(object callback, object options);
230 |
231 | The Relying Party MAY call the navigator.id.get method when it wishes to request that the User Agent generate an identity assertion as soon as it can. When this happens, the User Agent SHOULD pursue the following actions:
232 |
233 | 1. Establish the origin of the requesting site (including scheme and non-standard port).
234 | 1. Check local BrowserID store for known identities that have been successfully used previously.
235 | 1. Present the list of known identities. The User Agent MAY suggest a preferred identity out of that list based on heuristics or other internal state, e.g. the email last used on that site.
236 | 1. When the user selects an Identity:
237 | - check that the associated certificate is still valid. If not, initiate a provisioning workflow for that Identity, then continue once it returns successfully.
238 | - generate an Identity Assertion using the requesting site's origin as audience and the current time. Bundle with the associated certificate to create a Backed Identity Assertion, and invoke the callback with, as first and only parameter, a serialization of the Backed Identity Assertion, then terminate the login workflow.
239 | 1. If no Identities are known, or if the user wishes to use a new Identity, the User Agent should prompt the user for this new identity and use it to initiate a Provisioning workflow (see below). Once provisioning has completed, the User Agent SHOULD present the updated list of identities to the user.
240 | 1. If, at any point, the user cancels the login process, the User Agent SHOULD invoke the callback with a single null argument and terminate the login workflow.
241 |
242 | By the end of the process, the User Agent MUST invoke the callback with either a Backed Identity Assertion or null as first parameter.
243 |
244 | ### Provisioning ###
245 |
246 | The User Agent should support a provisioning workflow when a user wants to authenticate with a new email address. A provisioning workflow is initiated with some context:
247 |
248 | * the email address being provisioned
249 | * information about the security status of the session (user's own computer, shared computer, public computer, ...)
250 | * whether the authentication workflow has been invoked yet (initially false).
251 |
252 | During a provisioning action, the User Agent MUST support the following API calls:
253 |
254 | navigator.id.beginProvisioning(object callback)
255 |
256 | The User Agent SHOULD expect the callback function to accept parameters email and cert_duration_s.
257 |
258 | In response to this call, the User Agent should invoke the callback with parameters based on the provisioning context. The email parameter MUST be the email address which the user-agent is attempting to provision. The cert_duration_s parameter should be the requested validity duration for the certificate, which the User Agent SHOULD determine based on the security level of the session. For example, public computers should have very short certificate validity.
259 |
260 | navigator.id.genKeyPair(object callback);
261 |
262 | The User Agent SHOULD expect the callback to accept parameter pubkey, a serialized public-key string as per the above public-key spec.
263 |
264 | In response to this call, the User Agent MUST generate a fresh keypair associated with the email address for this provisioning context. The secret key should be stored internally, and the callback should be invoked with the serialized public-key as sole argument.
265 |
266 | navigator.id.registerCertificate(certificate);
267 |
268 | The User Agent SHOULD expect the certificate to be a valid serialized certificate, as per the above spec. The User Agent SHOULD expect the trust root for this certificate to comply with the characteristics described in the "Acceptable Trust Paths" section.
269 |
270 | The User Agent MUST associate this certificate with the email address for this provisioning context and store this association internally for later issuance of Backed Identity Assertions.
271 |
272 | navigator.id.raiseProvisioningFailure(string reason);
273 |
274 | The User Agent MUST interrupt this provisioning workflow.
275 |
276 | If the context indicates that the authentication workflow has already been invoked, then the User Agent SHOULD return from this workflow indicating failure to authenticate the user.
277 |
278 | If the context indicates that the authentication workflow has NOT already been invoked, then the User Agent SHOULD begin the Authentication Workflow (described below).
279 |
280 | #### WebIDL ####
281 |
282 |
283 | module navigator {
284 | module id {
285 | void beginProvisioning(object callback);
286 | void genKeyPair(string email, object callback);
287 | void registerCertificate(string certificate);
288 | void raiseProvisioningFailure(string reason);
289 | }
290 | };
291 |
292 |
293 | ### Authenticating ###
294 |
295 | The User Agent MUST support an authentication workflow when a user wants to certify a new email address but has failed the provisioning workflow. An authentication workflow is initiated with some context:
296 |
297 | * the email address which requires authentication
298 |
299 | During an authentication workflow, the User Agent MUST support the following API calls:
300 |
301 | navigator.id.beginAuthentication(object callback);
302 |
303 | The User Agent SHOULD expect a callback function as parameter to this API call.
304 |
305 | When this function is invoked, the User Agent MUST invoke the callback function, passing to it the context's email address as parameter.
306 |
307 | navigator.id.completeAuthentication();
308 |
309 | When this function is invoked, the User Agent MUST return to its provisioning workflow, retrieving the appropriate context for that provisioning workflow, with the added flag authenticationPerformed = true.
310 |
311 | navigator.id.raiseAuthenticationFailure(string reason);
312 |
313 | When this function is invoked, the User Agent MUST return to its provisioning workflow and proceed with the failure case.
314 |
315 | #### WebIDL ####
316 |
317 |
318 | module navigator {
319 | module id {
320 | void beginAuthentication(object callback);
321 | void completeAuthentication();
322 | void raiseAuthenticationFailure(string reason);
323 | }
324 | };
325 |
326 |
327 | Primary Authority Compliance
328 | --
329 |
330 | This section is normative.
331 |
332 | A primary authority MUST:
333 | * declare support and parameters for BrowserID
334 | * provide a user-authentication web flow
335 | * provide a user-key-certification web flow
336 |
337 | ### BrowserID Support Document ###
338 |
339 | A BrowserID support document MUST be a well-formed JSON document with at least these three fields: public-key, authentication, and provisioning. The document MAY contain additional JSON fields.
340 |
341 | The value of the public-key field MUST be a Public Key serialized as a JSON object, as defined above.
342 |
343 | The value of the authentication field MUST be a relative reference to a URI, as defined by [RFC3986](https://tools.ietf.org/html/rfc3986).
344 |
345 | The value of the provisioning field MUST also be a relative reference to a URI.
346 |
347 | #### BrowserID Delegated Support Document ####
348 |
349 | A BrowserID delegated-support document MUST be a well-formed JSON document with at least one field: authority. This field MUST be a domain name.
350 |
351 | ### Declaring Support and Parameters for BrowserID ###
352 |
353 | To declare support for BrowserID, a domain MUST publish either a BrowserID support document OR a BrowserID delegated-support document at a specific URI relative to the domain's SSL URI. The relative reference URI for this document is /.well-known/browserid, as per [RFC5785](https://tools.ietf.org/html/rfc5785). The domain MAY choose to reference this BrowserID support document from a host-meta file (as per RFC5785).
354 |
355 | The BrowserID support document (or delegated-support document) MUST be served with Content-Type application/json.
356 |
357 | The BrowserID support document (or delegated-support document) MAY be served with cache headers to indicate longevity of the BrowserID support parameters.
358 |
359 | ### Authenticating Users ###
360 |
361 | A BrowserID-compliant domain MUST provide a user-authentication web flow starting at the URI referenced by the authentication field in its published BrowserID support document. The specifics of the user-authentication flow are up to the domain. The flow MAY use redirects to other pages, even other domains, to complete the user authentication process. The flow SHOULD NOT use window.open() or other techniques that target new windows/tabs.
362 |
363 | The domain MAY serve this authentication workflow using anti-framing directives (e.g. X-FRAMES-OPTION).
364 |
365 | The authentication flow MUST complete at a URI relative to the BrowserID-compliant domain. The completion page content MUST include a JavaScript call to either navigator.id.completeAuthentication() if authentication was successful or navigator.id.raiseAuthenticationFailure() if the use cancelled authentication.
366 |
367 | ### Certifying Users ###
368 |
369 | A BrowserID-compliant domain MUST provide user-key certification at the URI referenced by the provisioning field in its published BrowserID support document.
370 |
371 | The domain SHOULD deliver, at that URI, an HTML document with either embedded or reference JavaScript, which it can expect to be evaluated in a standard user-agent frame. The domain SHOULD NOT use anti-framing directives (e.g. X-FRAMES-OPTION) when that URI is requested.
372 |
373 | The domain SHOULD determine, without any user-facing content, the user's state of authentication with the domain. The domain MAY use cookies or localStorage to make this determination.
374 |
375 | The domain MUST call, in JavaScript:
376 |
377 | navigator.id.beginProvisioning(provisionEmailFunction);
378 |
379 | with provisionEmailFunction a function that accepts an email address and a duration (in integral seconds) as parameter.
380 |
381 | Once the requested email provided as parameter to the provisionEmailFunction, the domain SHOULD check that the user is properly authenticated to use this email address. If she isn't, the domain SHOULD call:
382 |
383 | navigator.id.raiseProvisioningFailure(explanation)
384 |
385 | with explanation a string explaining the failure. The domain SHOULD conclude all JavaScript activity after making this call.
386 |
387 | You SHOULD use one of the following explanation codes:
388 | * user is not authenticated as target user - Indicates UA should show sign in screen again, due to an error
389 |
390 | If the user is properly authenticated, the domain MUST call:
391 |
392 | navigator.id.genKeyPair(gotPublicKey);
393 |
394 | with gotPublicKey a function that accepts a public-key JSON object.
395 |
396 | The domain's JavaScript SHOULD send this public-key to the domain's backend server. The domain's backend server SHOULD certify this key along with the email address provided to its provisionEmailFunction function, and an expiration date at least 1 minute in the future. The backend server MUST NOT issue a certificate valid longer than 24 hours. The backend server SHOULD NOT issue a certificate valid longer than the duration passed to the provisionEmailFunction earlier. The domain's backend server SHOULD deliver the generated Identity Certificate back to its JavaScript context. The domain's JavaScript MUST finally call:
397 |
398 | navigator.id.registerCertificate(certificate);
399 |
400 | with the Identity Certificate string.
401 |
402 | Assertion Verification
403 | -
404 |
405 | Backed Identity Assertions SHOULD NOT be verified in the client, in JavaScript or otherwise, since client runtimes may be altered to circumvent such verification. Instead, Backed Identity Assertions SHOULD be sent to a trusted server for verification.
406 |
407 | To verify a Backed Identity Assertion, a Relying Party SHOULD perform the following checks:
408 |
409 | 1. If the exp date of the assertion is earlier than the current time by more than a certain interval, the assertion has expired and must be rejected. A Relying Party MAY choose the length of that interval, though it is recommended that it be less than 5 minutes.
410 | 1. If the audience field of the assertion does not match the Relying Party's origin (including scheme and optional non-standard port), reject the assertion. A domain that includes the standard port, of 80 for HTTP and 443 for HTTPS, SHOULD be treated as equivalent to a domain that matches the protocol but does not include the port. (XXX: Can we find an RFC that defines this equality test?)
411 | 1. If the Identity Assertion's signature does not verify against the public-key within the last Identity Certificate, reject the assertion.
412 | 1. If there is more than one Identity Certificate, then reject the assertion unless each certificate after the first one is properly signed by the prior certificate's public key.
413 | 1. If the first certificate (or only certificate when there is only one) is not properly signed by the expected issuer's public key, reject the assertion. The expected issuer is either the domain of the certified email address in the last certificate, or the issuer listed in the first certificate if the email-address domain does not support BrowserID.
414 | 1. If the expected issuer was designated by the certificate rather than discovered given the user's email address, then the issuer SHOULD be login.persona.org, otherwise reject the assertion.
415 |
416 | A relying party MAY use a verification service that performs these steps and returns a summary of results. In that case, the verification service MUST perform all the checks described here. In order to perform audience checking, the verification service SHOULD require that the relying party indicate the expected value of the audience parameter.
417 |
418 | Security Considerations
419 | -
420 |
421 | things to write about:
422 |
423 | * certificate validity period
424 | * UAs reusing a key to get a new certificate
425 | * timing attacks
426 | * javascript implementations, good RNGs
427 |
428 | References
429 | --
430 |
431 | * JWT: http://self-issued.info/docs/draft-jones-json-web-token-04.html
432 | * JWK: http://self-issued.info/docs/draft-jones-json-web-key.html
433 |
--------------------------------------------------------------------------------