Easy & Secure Web Login with Mozilla Persona

Ben Adida

slides initially by Dan Callahan

Easy & Secure Web Login

with Mozilla Persona

Ben Adida
ben@adida.net · @benadida · ben.adida.net
slides initially by Dan Callahan



  1. The Problem: passwords suck
  2. The Mission: users choose
  3. The Solution: Mozilla Persona

The Problem:
passwords suck

The Mission:
users choose

The Mozilla Manifesto

The Solution:
Mozilla Persona

Words Boring. Demos Fun.

Persona is...

... an easy & secure login system without lockin.

Persona on your site means...

One button.

Any email address.

Sign in with Persona

Implementing Persona

RTFM

developer.mozilla.org/persona

Four Steps

1. JavaScript Library

      <script src="https://login.persona.org/include.js"></script>
    

2. Login / logout buttons

      $('#loginBtn').click( function () { navigator.id.request() } );
$('#logoutBtn').click( function () { navigator.id.logout() } );
    

3. Configure Persona

      navigator.id.watch({


                                                                









});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: ...,
  onlogin: function (assertion) {
                                                                



  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: "alice@example.com",
  onlogin: function (assertion) {
                                                                



  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: "bob@foobar.test",
  onlogin: function (assertion) {
                                                                



  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
                                                                



  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.



  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.
    $.post("/login", {"assertion": assertion})


  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.
    $.post("/login", {"assertion": assertion})
    .done(function () { window.location.reload() })

  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.
    $.post("/login", {"assertion": assertion})
    .done(function () { window.location.reload() })
    .fail(function () { navigator.id.logout() });
  },
  onlogout: function () {



  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.
    $.post("/login", {"assertion": assertion})
    .done(function () { window.location.reload() })
    .fail(function () { navigator.id.logout() });
  },
  onlogout: function () {
    // A user has logged out! Tear down their session.


  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.
    $.post("/login", {"assertion": assertion})
    .done(function () { window.location.reload() })
    .fail(function () { navigator.id.logout() });
  },
  onlogout: function () {
    // A user has logged out! Tear down their session.
    $.post("/logout")

  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.
    $.post("/login", {"assertion": assertion})
    .done(function () { window.location.reload() })
    .fail(function () { navigator.id.logout() });
  },
  onlogout: function () {
    // A user has logged out! Tear down their session.
    $.post("/logout")
    .always(function () { window.location.reload() });
  }
});
    

3. Configure Persona

      navigator.id.watch({
  loggedInUser: null,
  onlogin: function (assertion) {
    // A user wants to log in! Send the assertion to my backend.
    $.post("/login", {"assertion": assertion})
    .done(function () { window.location.reload() })
    .fail(function () { navigator.id.logout() });
  },
  onlogout: function () {
    // A user has logged out! Tear down their session.
    $.post("/logout")
    .always(function () { window.location.reload() });
  }
});
    
      @app.route("/logout", methods=["POST"])
def logout():
    session.clear()
    return Response(status=204)
    

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():


                                                            








    

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():
    # Send the assertion to Mozilla's verifier service

                                                            








    

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():
    # Send the assertion to Mozilla's verifier service
    data = {"assertion": request.form["assertion"],
            "audience": "https://example.com:443"}
                                                            







    

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():
    # Send the assertion to Mozilla's verifier service
    data = {"assertion": request.form["assertion"],
            "audience": "https://example.com:443"}
    resp = post("https://verifier.login.persona.org/verify",
                data=data)






    

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():
    # Send the assertion to Mozilla's verifier service
    data = {"assertion": request.form["assertion"],
            "audience": "https://example.com:443"}
    resp = post("https://verifier.login.persona.org/verify",
                data=data)
    info = resp.json()





    
      {
  "status": "okay",
  "email": "bob@foobar.test",
  "audience": "https://example.com:443",
  "expires": 1308859352261,
  "issuer": "foobar.test"
}

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():
    # Send the assertion to Mozilla's verifier service
    data = {"assertion": request.form["assertion"],
            "audience": "https://example.com:443"}
    resp = post("https://verifier.login.persona.org/verify",
                data=data)
    info = resp.json()





    

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():
    # Send the assertion to Mozilla's verifier service
    data = {"assertion": request.form["assertion"],
            "audience": "https://example.com:443"}
    resp = post("https://verifier.login.persona.org/verify",
                data=data)
    info = resp.json()

    if info["status"] != "okay":
      abort(403)


    

4. Verify assertions

      @app.route("/login", methods=["POST"])
def login():
    # Send the assertion to Mozilla's verifier service
    data = {"assertion": request.form["assertion"],
            "audience": "https://example.com:443"}
    resp = post("https://verifier.login.persona.org/verify",
                data=data)
    info = resp.json()

    if info["status"] != "okay":
      abort(403)

    session["email"] = info["email"]
    return Response(status=204)

Implementing Persona

  1. Thirty lines of code.
  2. One afternoon.

Small Request

Small Request

Questions?

persona.org

benadida@mozilla.com · @benadida · ben.adida.net

Creative Commons Photos By...