Chrome 70から指紋による認証をWebで行えるようになる。 認証の呼び出しにはWeb Authentication APIを使うということで、調べた。
©2018 Wantedly, Inc.θϩ͔ΒֶͿWeb Authentication APImeguro.es #1704.Oct.2018 - Naoki Kobayashi
View Slide
©2018 Wantedly, Inc.Naoki KobayashiGitHub: @kobayangTwitter: @kbys_02I’m an Engineer @Wantedlyࣾձਓ6ϲ݄͗ͨ͢React / Rails
©2018 Wantedly, Inc.Motivation- Chrome 70͔Βmacͷࢦೝূ͕WebͰ͑ΔΑ͏ʹͳΔɻ͍͢͝ɻ- Web Authentication API Λ͏Β͍͠https://blog.chromium.org/2018/09/chrome-70-beta-shape-detection-web.html
©2018 Wantedly, Inc.Web Authentication API?
©2018 Wantedly, Inc.Web Authentication APIWeb Authentication API ެ։伴҉߸Λ༻͍ͯڧྗͳೝূΛߦ͏Credential Management API ͷ֦ுػೳͰɺύεϫʔυΛ༻͍ͳ͍ೝূʹՃ͑ɺSMS Λ༻͍ͳ͍ೋཁૉೝূΛ࣮ݱ͠·͢ɻhttps://developer.mozilla.org/ja/docs/Web/API/Web_Authentication_APIWeb Authentication API Credential Management APIͷ֦ுΒ͍͠
©2018 Wantedly, Inc.Credential Management API???
©2018 Wantedly, Inc.Credential Management APIhttps://developers.google.com/web/fundamentals/security/credential-management/?hl=ja- ϩάΠϯϑϩʔΛγϯϓϧʹ- Ϣʔβʔɺηογϣϯͷظݶ͕Ε͍ͯΔ߹Ͱɺ αΠτʹࣗಈతʹ࠶ϩάΠϯͰ͖Δɻ- Account Chooser Λ༻ͯ͠ϫϯλοϓͰϩάΠϯͰ͖Δ- ωΠςΟϒͷ Account Chooser ͕දࣔ͞ΕΔͨΊɺ ϩάΠϯ ϑΥʔϜෆཁɻ- ೝূใΛอଘ- Ϣʔβʔ໊ͱύεϫʔυͷΈ߹Θͤɺ ·ͨϑΣσϨʔγϣϯ ΞΧϯτͷৄࡉΛอଘͰ͖Δɻ
©2018 Wantedly, Inc.DEMO:https://credential-management-sample.appspot.com/?hl=ja
©2018 Wantedly, Inc.Base of Credential Management API- navigator.credentials.store- ҾͰͨ͠ೝূใΛอଘ͢Δ- PasswordCredential (ύεϫʔυ)ɺ FederatedCredential (GoogleϩάΠϯͳͲʣͷೝূใΛͤΔ- PromiseͰฦΔ- navigator.credentials.get- อଘͨ͠APIΛऔಘ͢Δ- OptionͰΞΧϯτνϡʔβʔΛݺͼग़ͨ͠ΓͰ͖Δ- PromiseͰฦΔ
©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();});});
©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Ͱొ͢Δ
©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();});});ొʹޭͨ͠ΒొใΛอଘ͢Δࣦഊͨ͠߹ɺอଘͤͣΞϥʔτͳͲࣦഊॲཧΛॻ͘
©2018 Wantedly, Inc.e.g. authenticationvar 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” ΛΫϦοΫͨ࣌͠ͷࣗಈϩάΠϯΛ࣮ݱ͢Δ
©2018 Wantedly, Inc.e.g. authenticationvar 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});});}}
©2018 Wantedly, Inc.e.g. authenticationvar 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 Λ໌ࣔతʹग़͔͢Ͳ͏͔ΛࢦఆͰ͖Δ
©2018 Wantedly, Inc.Usage of Credential Management API- Registration Phase- FormͷೖྗͳͲ͔Βऔಘͨ͠ೝূใ͔ΒαʔϏεʹొ- ొʹޭͨ͠߹ʹ credentials.store ʹೝূใΛొ͢Δ- ೝূใʹ ύεϫʔυ ͱ ϑΣσϨʔγϣϯ(GoogleϩάΠϯͳͲʣ͕༻Ͱ͖Δ- Authentication Phase- credentials.store ʹΑͬͯอଘ͞ΕͨೝূใΛऔಘ͢Δ- औಘͨ͠ೝূใͰαʔϏεʹϩάΠϯΛࢼΈΔ- ޭͨ͠߹ʹϩάΠϯॲཧΛɺࣦഊͨ͠߹ϩάΠϯը໘ʹભҠ
©2018 Wantedly, Inc.Web Authentication APIʢ΄Μ͍ͩʣ
©2018 Wantedly, Inc.Extend Web Authentication API- PublicKeyCredential- ެ։伴ʹΑΔೝূ- navigator.credentials ʹ Password, Federation Ҏ֎ʹɺ PublicKeyCredential ΛͤΔ- navigator.credentials.create- ඇಉظͰೝূใΛऔಘͰ͖Δ- AuthenticatorʢࢦೝূͳͲʣ͔ΒೝূใΛऔಘ͢Δ
©2018 Wantedly, Inc.ެ։伴ʹΑΔೝূ- Registration Phase- Authenticator ͕ެ։伴ͱൿີ伴ͷϖΞΛੜ- ެ։伴Λαʔόʔʹొ- (Web Authentication APIͰɺಉ࣌ʹೝূߦͬͯΔʣ- Authentication Phase- αʔό͕νϟϨϯδʢେ͖ͳཚʣΛΫϥΠΞϯτʹ͢- ΫϥΠΞϯτ͕ൿີ伴ͰνϟϨϯδΛ҉߸Խͯ͠αʔόʹฦ͢- ΫϥΠΞϯτʹඥ͍ͮͨެ։伴Ͱ҉߸Λ෮߸Ͱ͖Δ͔ݕূ
©2018 Wantedly, Inc.Registration flow
©2018 Wantedly, Inc.Authentication flow
©2018 Wantedly, Inc.Web Authentication API Ͱ FIDOU2F(YubiKey) ೝূ:https://blog.jxck.io/entries/2018-05-15/webauthentication-api.html- Yubikey AuthenticatorΛͬͨೝূํ๏- ͘͢͝ৄ͍͠
©2018 Wantedly, Inc.Registration credentials with Authenticatorvar publicKey = {// The challenge is produced by the server; see the Security Considerationschallenge: challenge,// Relying Party:rp: {name: "ACME Corporation",},// Useruser: {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 minutetimeout: 60000,// No exclude list of PKCredDescriptorsexcludeCredentials: [],// Include location informationextensions: { 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
©2018 Wantedly, Inc.Authentication by Authenticatorvar options = {// The challenge is produced by the server;challenge: challenge,// 1 minutetimeout: 60000,// credentialId is generated by the authenticatorallowCredentials: [{ 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-authenticationhttps://w3c.github.io/webauthn/#sample-authentication
©2018 Wantedly, Inc.ࢦೝূΛWebϩάΠϯͰ͍͍ͨ- Chrome 70 beta ͔Βೝূใ࡞࣌ʹ Authenticator ΛબͰ͖Δ- Authenticator ͱͯ͠ɺࢦʹΑΔೝূΛબͰ͖Δ
©2018 Wantedly, Inc.·ͱΊ- Web Authentication API Credential Management API ͷ֦ு- Credential Management API ೝূใͷ֨ೲɾऔಘΛߦ͍ ϩάΠϯϑϩʔΛ؆ܿʹͰ͖Δ- Web Authentication API Authenticator Λݺͼग़ͯ͠ ҉߸伴ೝূʹΑΔϩάΠϯ͕࣮ݱͰ͖Δ- Chrome 70͔ΒࢦೝূΛWeb Authentication API͔Βݺͼग़ͤΔ- ઐ༻ͷσόΠε͕ඞཁͳ͘ͳΔͷͰύεϫʔυϨεͷ͕࣌͘Δ͔ʁ
©2018 Wantedly, Inc.https://www.wantedly.com/projects/239714