Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ゼロから学ぶWeb Authentication API

kobayang
October 04, 2018

ゼロから学ぶWeb Authentication API

Chrome 70から指紋による認証をWebで行えるようになる。
認証の呼び出しにはWeb Authentication APIを使うということで、調べた。

kobayang

October 04, 2018
Tweet

More Decks by kobayang

Other Decks in Technology

Transcript

  1. ©2018 Wantedly, Inc.
    θϩ͔ΒֶͿ
    Web Authentication API
    meguro.es #17
    04.Oct.2018 - Naoki Kobayashi

    View full-size slide

  2. ©2018 Wantedly, Inc.
    Naoki Kobayashi
    GitHub: @kobayang
    Twitter: @kbys_02
    I’m an Engineer @Wantedly
    ࣾձਓ6ϲ݄͗ͨ͢
    React / Rails

    View full-size slide

  3. ©2018 Wantedly, Inc.
    Motivation
    - Chrome 70͔Βmacͷࢦ໲ೝূ͕WebͰ࢖͑ΔΑ͏ʹͳΔɻ͍͢͝ɻ
    - Web Authentication API Λ࢖͏Β͍͠
    https://blog.chromium.org/2018/09/chrome-70-beta-shape-detection-web.html

    View full-size slide

  4. ©2018 Wantedly, Inc.
    Web Authentication API?

    View full-size slide

  5. ©2018 Wantedly, Inc.
    Web Authentication API
    Web Authentication API ͸ެ։伴҉߸Λ༻͍ͯڧྗͳೝূΛߦ͏
    Credential Management API ͷ֦ுػೳͰɺ
    ύεϫʔυΛ༻͍ͳ͍ೝূʹՃ͑ɺSMS Λ༻͍ͳ͍ೋཁૉೝূΛ࣮ݱ͠·͢ɻ
    https://developer.mozilla.org/ja/docs/Web/API/Web_Authentication_API
    Web Authentication API͸

    Credential Management APIͷ֦ுΒ͍͠

    View full-size slide

  6. ©2018 Wantedly, Inc.
    Credential Management API???

    View full-size slide

  7. ©2018 Wantedly, Inc.
    Credential Management API
    https://developers.google.com/web/fundamentals/security/credential-management/?hl=ja
    - ϩάΠϯϑϩʔΛγϯϓϧʹ
    - Ϣʔβʔ͸ɺηογϣϯͷظݶ͕੾Ε͍ͯΔ৔߹Ͱ΋ɺ

    αΠτʹࣗಈతʹ࠶౓ϩάΠϯͰ͖Δɻ
    - Account Chooser Λ࢖༻ͯ͠ϫϯλοϓͰϩάΠϯͰ͖Δ
    - ωΠςΟϒͷ Account Chooser ͕දࣔ͞ΕΔͨΊɺ

    ϩάΠϯ ϑΥʔϜ͸ෆཁɻ
    - ೝূ৘ใΛอଘ
    - Ϣʔβʔ໊ͱύεϫʔυͷ૊Έ߹Θͤɺ

    ·ͨ͸ϑΣσϨʔγϣϯ ΞΧ΢ϯτͷৄࡉΛอଘͰ͖Δɻ

    View full-size slide

  8. ©2018 Wantedly, Inc.
    DEMO:
    https://credential-management-
    sample.appspot.com/?hl=ja

    View full-size slide

  9. ©2018 Wantedly, Inc.
    Base of Credential Management API
    - navigator.credentials.store
    - Ҿ਺Ͱ౉ͨ͠ೝূ৘ใΛอଘ͢Δ
    - PasswordCredential (ύεϫʔυ)ɺ

    FederatedCredential (GoogleϩάΠϯͳͲʣͷೝূ৘ใΛ౉ͤΔ
    - PromiseͰฦΔ
    - navigator.credentials.get
    - อଘͨ͠APIΛऔಘ͢Δ
    - OptionͰΞΧ΢ϯτνϡʔβʔΛݺͼग़ͨ͠ΓͰ͖Δ
    - PromiseͰฦΔ

    View full-size slide

  10. ©2018 Wantedly, Inc.
    e.g. registration (PasswordCredential)
    var regForm = document.querySelector('#regForm');
    regForm.addEventListener('submit', function(e) {
    e.preventDefault();
    fetch('/register', {
    method: 'POST',
    credentials: 'include',
    body: new FormData(regForm)
    }).then(function(res) {
    if (res.status === 200) {
    if (!!window.PasswordCredential) {
    var cred = new PasswordCredential(regForm);
    navigator.credentials.store(cred)
    .then(function() {
    location.href = '/main?quote=You are registered';
    });
    } else {
    location.href = '/main?quote=You are registered';
    }
    } else {
    alertRegistrationFailed();
    }
    }, function() {
    alertRegistrationFailed();
    });
    });

    View full-size slide

  11. ©2018 Wantedly, Inc.
    e.g. registration (PasswordCredential)
    var regForm = document.querySelector('#regForm');
    regForm.addEventListener('submit', function(e) {
    e.preventDefault();
    fetch('/register', {
    method: 'POST',
    credentials: 'include',
    body: new FormData(regForm)
    }).then(function(res) {
    if (res.status === 200) {
    if (!!window.PasswordCredential) {
    var cred = new PasswordCredential(regForm);
    navigator.credentials.store(cred)
    .then(function() {
    location.href = '/main?quote=You are registered';
    });
    } else {
    location.href = '/main?quote=You are registered';
    }
    } else {
    alertRegistrationFailed();
    }
    }, function() {
    alertRegistrationFailed();
    });
    });
    Formͷ৘ใ͔Βొ࿥Λߦ͏
    > ొ࿥͞ΕͨσʔλΛ࢖͍͍ͨͷͰ

    > Formͷ”submit” ͸ͦͷ··ߦΘͣ
    > fetchͰొ࿥͢Δ

    View full-size slide

  12. ©2018 Wantedly, Inc.
    e.g. registration (PasswordCredential)
    var regForm = document.querySelector('#regForm');
    regForm.addEventListener('submit', function(e) {
    e.preventDefault();
    fetch('/register', {
    method: 'POST',
    credentials: 'include',
    body: new FormData(regForm)
    }).then(function(res) {
    if (res.status === 200) {
    if (!!window.PasswordCredential) {
    var cred = new PasswordCredential(regForm);
    navigator.credentials.store(cred)
    .then(function() {
    location.href = '/main?quote=You are registered';
    });
    } else {
    location.href = '/main?quote=You are registered';
    }
    } else {
    alertRegistrationFailed();
    }
    }, function() {
    alertRegistrationFailed();
    });
    });
    ొ࿥ʹ੒ޭͨ͠Β
    ొ࿥৘ใΛอଘ͢Δ
    ࣦഊͨ͠৔߹ɺอଘ͸ͤͣ
    ΞϥʔτͳͲࣦഊॲཧΛॻ͘

    View full-size slide

  13. ©2018 Wantedly, Inc.
    e.g. authentication
    var signin = document.querySelector('#signin');
    signin.addEventListener('click', function() {
    autoSignIn(‘optional’)
    .then(function() {
    location.href = '/main?quote=You are signed in';
    }, function() {
    location.href = '/signin';
    });
    });
    “SIGN IN” ΛΫϦοΫͨ࣌͠ͷࣗಈϩάΠϯΛ࣮ݱ͢Δ

    View full-size slide

  14. ©2018 Wantedly, Inc.
    e.g. authentication
    var autoSignIn = function(mode) {
    if (!!window.PasswordCredential) {
    return navigator.credentials.get({
    password: true,
    federated: {
    providers: [ GOOGLE_SIGNIN ]
    },
    mediation: mode
    }).then(function(cred) {
    if (cred) {
    /* authentication */

    return Promise.reject();
    } else {
    return Promise.reject();
    }
    }).then(function(res) {
    if (res.status === 200) {
    return Promise.resolve();
    } else {
    return Promise.reject();
    }
    });
    } else {
    return Promise.reject();
    }
    };
    /* authentication */
    var form = new FormData();
    var csrf_token = document.querySelector('#csrf_token').value;
    form.append('csrf_token', csrf_token);
    switch (cred.type) {
    case 'password':
    form.append('email', cred.id);
    form.append('password', cred.password);
    return fetch('/auth/password', {
    method: 'POST',
    credentials: 'include',
    body: form
    });
    case 'federated':
    switch (cred.provider) {
    case GOOGLE_SIGNIN:
    return gSignIn(cred.id)
    .then(function(googleUser) {
    var id_token = googleUser.getAuthResponse().id_token;
    form.append('id_token', id_token);
    return fetch('/auth/google', {
    method: 'POST',
    credentials: 'include',
    body: form
    });
    });
    }
    }

    View full-size slide

  15. ©2018 Wantedly, Inc.
    e.g. authentication
    var autoSignIn = function(mode) {
    if (!!window.PasswordCredential) {
    return navigator.credentials.get({
    password: true,
    federated: {
    providers: [ GOOGLE_SIGNIN ]
    },
    mediation: mode
    }).then(function(cred) {
    if (cred) {
    /* authentication */

    return Promise.reject();
    } else {
    return Promise.reject();
    }
    }).then(function(res) {
    if (res.status === 200) {
    return Promise.resolve();
    } else {
    return Promise.reject();
    }
    });
    } else {
    return Promise.reject();
    }
    };
    `mediation` Ͱ Account Chooser Λ
    ໌ࣔతʹग़͔͢Ͳ͏͔ΛࢦఆͰ͖Δ

    View full-size slide

  16. ©2018 Wantedly, Inc.
    e.g. authentication
    var autoSignIn = function(mode) {
    if (!!window.PasswordCredential) {
    return navigator.credentials.get({
    password: true,
    federated: {
    providers: [ GOOGLE_SIGNIN ]
    },
    mediation: mode
    }).then(function(cred) {
    if (cred) {
    /* authentication */

    return Promise.reject();
    } else {
    return Promise.reject();
    }
    }).then(function(res) {
    if (res.status === 200) {
    return Promise.resolve();
    } else {
    return Promise.reject();
    }
    });
    } else {
    return Promise.reject();
    }
    };
    /* authentication */
    var form = new FormData();
    var csrf_token = document.querySelector('#csrf_token').value;
    form.append('csrf_token', csrf_token);
    switch (cred.type) {
    case 'password':
    form.append('email', cred.id);
    form.append('password', cred.password);
    return fetch('/auth/password', {
    method: 'POST',
    credentials: 'include',
    body: form
    });
    case 'federated':
    switch (cred.provider) {
    case GOOGLE_SIGNIN:
    return gSignIn(cred.id)
    .then(function(googleUser) {
    var id_token = googleUser.getAuthResponse().id_token;
    form.append('id_token', id_token);
    return fetch('/auth/google', {
    method: 'POST',
    credentials: 'include',
    body: form
    });
    });
    }
    }

    View full-size slide

  17. ©2018 Wantedly, Inc.
    Usage of Credential Management API
    - Registration Phase
    - FormͷೖྗͳͲ͔Βऔಘͨ͠ೝূ৘ใ͔ΒαʔϏεʹొ࿥
    - ొ࿥ʹ੒ޭͨ͠৔߹ʹ credentials.store ʹೝূ৘ใΛొ࿥͢Δ
    - ೝূ৘ใʹ͸ ύεϫʔυ ͱ ϑΣσϨʔγϣϯ(GoogleϩάΠϯͳͲʣ͕࢖༻Ͱ͖Δ
    - Authentication Phase
    - credentials.store ʹΑͬͯอଘ͞Εͨೝূ৘ใΛऔಘ͢Δ
    - औಘͨ͠ೝূ৘ใͰαʔϏεʹϩάΠϯΛࢼΈΔ
    - ੒ޭͨ͠৔߹ʹ͸ϩάΠϯॲཧΛɺࣦഊͨ͠৔߹͸ϩάΠϯը໘ʹભҠ

    View full-size slide

  18. ©2018 Wantedly, Inc.
    Web Authentication API
    ʢ΄Μ͍ͩʣ

    View full-size slide

  19. ©2018 Wantedly, Inc.
    Extend Web Authentication API
    - PublicKeyCredential
    - ެ։伴ʹΑΔೝূ
    - navigator.credentials ʹ Password, Federation Ҏ֎ʹɺ

    PublicKeyCredential Λ౉ͤΔ
    - navigator.credentials.create
    - ඇಉظͰೝূ৘ใΛऔಘͰ͖Δ
    - Authenticatorʢࢦ໲ೝূͳͲʣ͔Βೝূ৘ใΛऔಘ͢Δ

    View full-size slide

  20. ©2018 Wantedly, Inc.
    ެ։伴ʹΑΔೝূ
    - Registration Phase
    - Authenticator ͕ެ։伴ͱൿີ伴ͷϖΞΛੜ੒
    - ެ։伴Λαʔόʔʹొ࿥
    - (Web Authentication APIͰ͸ɺಉ࣌ʹೝূ΋ߦͬͯΔʣ
    - Authentication Phase
    - αʔό͕νϟϨϯδʢେ͖ͳཚ਺ʣΛΫϥΠΞϯτʹ౉͢
    - ΫϥΠΞϯτ͕ൿີ伴ͰνϟϨϯδΛ҉߸Խͯ͠αʔόʹฦ͢
    - ΫϥΠΞϯτʹඥ͍ͮͨެ։伴Ͱ҉߸Λ෮߸Ͱ͖Δ͔ݕূ

    View full-size slide

  21. ©2018 Wantedly, Inc.
    Registration flow

    View full-size slide

  22. ©2018 Wantedly, Inc.
    Authentication flow

    View full-size slide

  23. ©2018 Wantedly, Inc.
    Web Authentication API Ͱ FIDO
    U2F(YubiKey) ೝূ:
    https://blog.jxck.io/entries/2018-05-15/
    webauthentication-api.html
    - Yubikey AuthenticatorΛ࢖ͬͨೝূํ๏
    - ͘͢͝ৄ͍͠

    View full-size slide

  24. ©2018 Wantedly, Inc.
    Registration credentials with Authenticator
    var publicKey = {
    // The challenge is produced by the server; see the Security Considerations
    challenge: challenge,
    // Relying Party:
    rp: {
    name: "ACME Corporation",
    },
    // User
    user: {
    id: userId,
    name: "[email protected]",
    displayName: "Alex P. Müller",
    icon: "https://pics.example.com/00/p/aBjjjpqPb.png",
    },
    // This Relying Party will accept either an ES256 or RS256 credential, but
    // prefers an ES256 credential.
    pubKeyCredParams: [
    {
    type: "public-key",
    alg: -7, // "ES256" as registered in the IANA COSE Algorithms registry
    },
    ],
    // 1 minute
    timeout: 60000,
    // No exclude list of PKCredDescriptors
    excludeCredentials: [],
    // Include location information
    extensions: { loc: true },
    };
    https://w3c.github.io/webauthn/#sample-authentication
    // Note: The following call will cause
    // the authenticator to display UI.
    navigator.credentials
    .create({ publicKey })
    .then(function(newCredentialInfo) {
    // Send new credential info to server
    // for verification and registration.
    })
    .catch(function(err) {
    // No acceptable authenticator or
    // user refused consent. Handle appropriately.
    });
    PublicKeyCredential

    View full-size slide

  25. ©2018 Wantedly, Inc.
    Authentication by Authenticator
    var options = {
    // The challenge is produced by the server;
    challenge: challenge,
    // 1 minute
    timeout: 60000,
    // credentialId is generated by the authenticator
    allowCredentials: [{ type: "public-key", id: credentialId }],
    };
    navigator.credentials
    .get({ publicKey: options })
    .then(function(assertion) {
    // Send assertion to server for verification
    })
    .catch(function(err) {
    // No acceptable credential or user refused consent.
    // Handle appropriately.
    });
    https://w3c.github.io/webauthn/#sample-authentication
    https://w3c.github.io/webauthn/#sample-authentication

    View full-size slide

  26. ©2018 Wantedly, Inc.
    ࢦ໲ೝূΛWebϩάΠϯͰ࢖͍͍ͨ
    - Chrome 70 beta ͔Β͸ೝূ৘ใ࡞੒࣌ʹ Authenticator Λબ୒Ͱ͖Δ
    - Authenticator ͱͯ͠ɺࢦ໲ʹΑΔೝূΛબ୒Ͱ͖Δ

    View full-size slide

  27. ©2018 Wantedly, Inc.
    ·ͱΊ
    - Web Authentication API ͸ Credential Management API ͷ֦ு
    - Credential Management API ͸ೝূ৘ใͷ֨ೲɾऔಘΛߦ͍

    ϩάΠϯϑϩʔΛ؆ܿʹͰ͖Δ
    - Web Authentication API ͸ Authenticator Λݺͼग़ͯ͠

    ҉߸伴ೝূʹΑΔϩάΠϯ͕࣮ݱͰ͖Δ
    - Chrome 70͔Βࢦ໲ೝূΛWeb Authentication API͔Βݺͼग़ͤΔ
    - ઐ༻ͷσόΠε͕ඞཁͳ͘ͳΔͷͰύεϫʔυϨεͷ࣌୅͕͘Δ͔΋ʁ

    View full-size slide

  28. ©2018 Wantedly, Inc.
    https://www.wantedly.com/projects/239714

    View full-size slide