Apple¤¬Ëèǯ³«ºÅ¤·¤Æ¤¤¤ë³«È¯¼Ô¸þ¤±²ñµÄ¡ÖWWDC 2020¡×¤Çȯɽ¤·¤¿¡ÖSafari¤Îǧ¾Úµ¡Ç½¤ÈFace ID¤ª¤è¤ÓTouch ID¤ÎÅý¹ç¡×¤Ï¡¢¥Ñ¥¹¥ï¡¼¥É¤ËÂå¤ï¤ë¿·¤·¤¤Ç§¾ÚÊý¼°¤Î¤Ò¤È¤Ä¤È¤·¤ÆSafari 14¤«¤é¼ÂÁõ¤µ¤ì¤Æ¤¤¤Þ¤¹¡£¤½¤Îǧ¾Úµ¡Ç½¤ÎÆ°ºî¥¤¥á¡¼¥¸¤È»ÅÁȤߤˤĤ¤¤Æ¡¢Apple¤¬³«È¯¤¹¤ëSafari¤Î¥ì¥ó¥À¥ê¥ó¥°¥¨¥ó¥¸¥ó¡ÖWebKit¡×¤Î³«È¯¤Ë·È¤ï¤ëJiewen Tan»á¤¬²òÀ⤷¤Æ¤¤¤Þ¤¹¡£

Meet Face ID and Touch ID for the Web | WebKit

https://webkit.org/blog/11312/meet-face-id-and-touch-id-for-the-web/

Meet Face ID and Touch ID for the web - WWDC 2020 - Videos - Apple Developer

https://developer.apple.com/videos/play/wwdc2020/10670/

½¾Íè¡¢¥¦¥§¥Ö¥µ¥¤¥È¤Ë¥í¥°¥¤¥ó¤¹¤ë¤¿¤á¤Îǧ¾ÚÊýË¡¤Ï¥Ñ¥¹¥ï¡¼¥É¤¬°ìÈÌŪ¤Ç¤·¤¿¤¬¡¢ÁíÅö¤¿¤ê¹¶·â¤Ê¤É¤Ç¥Ñ¥¹¥ï¡¼¥É¤¬Ï³¤¨¤¤¤·¡¢ÉÔÀµ¥í¥°¥¤¥ó¤µ¤ì¤ë¤È¤¤¤Ã¤¿¥»¥­¥å¥ê¥Æ¥£¾å¤Î·üÇ°¤¬À¸¤¸»Ï¤á¤Þ¤·¤¿¡£¥Ñ¥¹¥ï¡¼¥É¤ËÂå¤ï¤ëǧ¾ÚÊý¼°¤òÀ¸¤ß½Ð¤¹¤Ù¤¯¡¢À¸ÂÎǧ¾Ú¤ò´ðËܤȤ·¤¿¿·¤·¤¤¥ª¥ó¥é¥¤¥óǧ¾Úµ»½Ñ¤Îɸ½à²½ÃÄÂΡÖFIDO Alliance¡×¤¬É¸½à²½¤ò¿Ê¤á¤Æ¤¤¤ë¤Î¤¬¡ÖWebAuthn¡×¤Ç¡¢YubiKey¤Ê¤É¤Î¥»¥­¥å¥ê¥Æ¥£¥­¡¼¤ä»ØÌæǧ¾Ú¡¢´éǧ¾Ú¤Ê¤É¤Ë¤è¤ë¥¦¥§¥Ö¥µ¥¤¥È¤Î¥í¥°¥¤¥ó¤ò²Äǽ¤Ë¤¹¤ëµ»½Ñ¤Ç¤¹¡£

¥Ñ¥¹¥ï¡¼¥ÉÉÔÍ×¤Î¥í¥°¥¤¥óÊýË¡¡ÖWebAuthn¡×¤¬¥¦¥§¥Öɸ½à¤Ë¤Ê¤ë - GIGAZINE



Safari 14¤«¤é¼ÂÁõ¤µ¤ì¤¿Face ID¤äTouch ID¤Ë¤è¤ë¥í¥°¥¤¥óµ¡Ç½¤â¡¢¤³¤ÎWebAuthn¤ËÂбþ¤·¤¿¥¦¥§¥Ö¥µ¥¤¥È¤ÇÍøÍѲÄǽ¡£Î㤨¤Ð¡¢Safari¤ÇWebAuthn¤ËÂбþ¤·¤¿¥¦¥§¥Ö¥µ¥¤¥È¤Ç¥í¥°¥¤¥ó¤ò»î¤ß¤ë¤È¡¢°Ê²¼¤Î²èÁü¤Î¤è¤¦¤ËFace ID¤äTouch ID¤òÍøÍѤ·¤Æ¥í¥°¥¤¥ó¤ò¹Ô¤¦¤«¤É¤¦¤«¤Î¥Ý¥Ã¥×¥¢¥Ã¥×¤¬½Ð¸½¤·¤Þ¤¹¡£



¤³¤Î¥Ý¥Ã¥×¥¢¥Ã¥×¤ò½Ð¸½¤µ¤»¤Æ¤¤¤ë¤Î¤¬°Ê²¼¤ÎJavaScript¥³¡¼¥É¡£É¬¿Ü¤Îµ­½Ò¤Ï¡Öoptions¡×Æâ¤Î¡ÖauthenticatorSelection: { authenticatorAttachment: "platform" }¡×¤Ç¡¢¥×¥é¥Ã¥È¥Õ¥©¡¼¥à¤Ë¤è¤ëǧ¾Úµ¡Ç½¤Î¤ß¤òÍøÍѤ¹¤ë¤è¤¦¤ËWebKit¤Ë»Ø¼¨¤¹¤ëµ­½Ò¤Ç¤¢¤ë¤È¤Î¤³¤È¡£¥æ¡¼¥¶¡¼Ì¾¤äID¤Ê¤É¤òµ­½Ò¤·¤¿options¤ò»È¤Ã¤Æ¡¢navigator.credentials.create()¤Ë¤è¤Ã¤Æ¡¢¿·¤·¤¤Ç§¾Ú¾ðÊó¤ò¥í¡¼¥«¥ë¤Î¥Ç¥Ð¥¤¥¹¾å¤ËÀ¸À®¤·¤Æ¤¤¤Þ¤¹¡£

const options = {
publicKey: {
rp: { name: "example.com" },
user: {
name: "john.appleseed@example.com",
id: userIdBuffer,
displayName: "John Appleseed"
},
pubKeyCredParams: [ { type: "public-key", alg: -7 } ],
challenge: challengeBuffer,
authenticatorSelection: { authenticatorAttachment: "platform" }
}
};

const publicKeyCredential = await navigator.credentials.create(options);


¥¦¥§¥Ö¥µ¥¤¥È¤¬¥Ç¥Ð¥¤¥¹ÆâÉô¤Îǧ¾Ú¾ðÊó¤òÍøÍѤ·¤Æǧ¾Ú¤ò¹Ô¤¦ºÝ¤Î¥Ý¥Ã¥×¥¢¥Ã¥×¤Ï¤³¤ó¤Ê´¶¤¸¡£Ç§¾Ú¾ðÊ󤬤ҤȤĤ·¤«¤Ê¤¤¾ì¹ç¤Î²èÌ̤ϰʲ¼¡£



ǧ¾Ú¾ðÊó¤¬Ê£¿ô¸ºß¤¹¤ë¤È¡¢¥¢¥«¥¦¥ó¥È¤òÁªÂò¤¹¤ë¥Ý¥Ã¥×¥¢¥Ã¥×¤¬É½¼¨¤µ¤ì¤Þ¤¹¡£



¥Ç¥Ð¥¤¥¹¤Ë¤è¤ëǧ¾Ú¤ò¹Ô¤¦³Îǧ¥Ý¥Ã¥×¥¢¥Ã¥×¤ò½Ð¸½¤µ¤»¤ë¤Ë¤Ï¡¢°Ê²¼¤Î¤è¤¦¤Ê¥³¡¼¥É¤òÍѤ¤¤ë¤È¤Î¤³¤È¡£allowCredentialsÆâ¤Î¡Ötransports: ["internal"]¡×¤Ç¥Ç¥Ð¥¤¥¹ÆâÉô¤Îǧ¾Ú¾ðÊó¤òÍøÍѤ¹¤ë¤è¤¦¤ËÌÀ¼¨¤·¤Æ¤ª¤ê¡¢navigator.credentials.get()¤Çǧ¾Ú¾ðÊó¤ò¼èÆÀ¤·¤Æ¤¤¤Þ¤¹¡£

const options = {
publicKey: {
challenge: challengeBuffer,
allowCredentials: [
{ type: "public-key", id: credentialIdBuffer1, transports: ["internal"] },
// ... more Credential IDs can be supplied.
]
}
};

const publicKeyCredential = await navigator.credentials.get(options);


¡Ötransports: ["internal"]¡×¤¬»ØÄꤵ¤ì¤Æ¤¤¤Ê¤¤Ç§¾Ú¾ðÊ󤬤¢¤ë¾ì¹ç¤Ï¡¢°Ê²¼¤Î¤è¤¦¤Ë¥»¥­¥å¥ê¥Æ¥£¥­¡¼¤Ë¤è¤ëǧ¾Ú¤òÁªÂò¤Ç¤­¤ë¥Ý¥Ã¥×¥¢¥Ã¥×¤¬É½¼¨¤µ¤ì¤ë¤ÈTan»á¤ÏÀâÌÀ¤·¤Æ¤¤¤Þ¤¹¡£



¤³¤¦¤·¤¿Ç§¾Ú¤òµá¤á¤ë¥Ý¥Ã¥×¥¢¥Ã¥×¤¬¾¡¼ê¤Ëɽ¼¨¤µ¤ì¤Æ¤·¤Þ¤¦¤Î¤òËɤ°¤¿¤á¤Ë¡¢¥æ¡¼¥¶¡¼Â¦¤Ç²¿¤é¤«¤ÎÁàºî¤ò¹Ô¤Ã¤¿¸å¤Ë¥Ý¥Ã¥×¥¢¥Ã¥×¤òɽ¼¨¤¹¤ë¥µ¥ó¥×¥ë¥³¡¼¥É¤â¾Ò²ð¤µ¤ì¤Æ¤¤¤Þ¤¹¡£Î㤨¤Ð¡¢¤¢¤ë¥Ü¥¿¥ó¤¬¥¯¥ê¥Ã¥¯¤µ¤ì¤¿¤éǧ¾Ú¾ðÊó¤òÀ¸À®¤¹¤ë¥Ý¥Ã¥×¥¢¥Ã¥×¤òɽ¼¨¤¹¤ë¾ì¹ç¤Ï¡¢addEventListener()¤òÍѤ¤¤Æ°Ê²¼¤Î¤è¤¦¤Ë¥³¡¼¥É¤òµ­½Ò¤·¤Þ¤¹¡£

button.addEventListener("click", async () => {
const options = {
publicKey: {
...
challenge: challengeBuffer,
...
}
};

const publicKeyCredential = await navigator.credentials.create(options);
});


Tan»á¤Ï¡Ö¥Þ¥ë¥Á¥Õ¥¡¥¯¥¿¡¼Ç§¾Ú¤¬¥Ñ¥¹¥ï¡¼¥É¤À¤±¤Îǧ¾Ú¤òÃÖ¤­´¹¤¨¡¢¥¦¥§¥Ö¤Ë¤ª¤±¤ë°ìÈÌŪ¤Êǧ¾ÚÊýË¡¤Ë¤Ê¤ë¤È³Î¿®¤·¤Æ¤¤¤Þ¤¹¡×¤È¸ì¤Ã¤Æ¤ª¤ê¡¢³«È¯¼Ô¤ËÂФ·¤ÆFace ID¤ÈTouch ID¤Ë¤è¤ëǧ¾Úµ¡Ç½¤Î¥Æ¥¹¥È¤òµá¤á¤Æ¤¤¤Þ¤¹¡£