parent
							
								
									d1be8b43f6
								
							
						
					
					
						commit
						234294d564
					
				
					 4 changed files with 46 additions and 32 deletions
				
			
		|  | @ -414,7 +414,7 @@ passwordMatched: "一致しました" | |||
| passwordNotMatched: "一致していません" | ||||
| signinWith: "{x}でログイン" | ||||
| signinFailed: "ログインできませんでした。ユーザー名とパスワードを確認してください。" | ||||
| tapSecurityKey: "セキュリティーキーにタッチ" | ||||
| tapSecurityKey: "セキュリティキーにタッチ" | ||||
| or: "もしくは" | ||||
| uiLanguage: "UIの表示言語" | ||||
| groupInvited: "グループに招待されました" | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ import MkButton from './ui/button.vue'; | |||
| import MkInput from './ui/input.vue'; | ||||
| import i18n from '../i18n'; | ||||
| import { apiUrl, host } from '../config'; | ||||
| import { hexifyAB } from '../scripts/2fa'; | ||||
| import { byteify, hexify } from '../scripts/2fa'; | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	i18n, | ||||
|  | @ -121,14 +121,9 @@ export default Vue.extend({ | |||
| 			this.queryingKey = true; | ||||
| 			return navigator.credentials.get({ | ||||
| 				publicKey: { | ||||
| 					challenge: Buffer.from( | ||||
| 						this.challengeData.challenge | ||||
| 							.replace(/\-/g, '+') | ||||
| 							.replace(/_/g, '/'), | ||||
| 							'base64' | ||||
| 					), | ||||
| 					challenge: byteify(this.challengeData.challenge, 'base64'), | ||||
| 					allowCredentials: this.challengeData.securityKeys.map(key => ({ | ||||
| 						id: Buffer.from(key.id, 'hex'), | ||||
| 						id: byteify(key.id, 'hex'), | ||||
| 						type: 'public-key', | ||||
| 						transports: ['usb', 'nfc', 'ble', 'internal'] | ||||
| 					})), | ||||
|  | @ -143,9 +138,9 @@ export default Vue.extend({ | |||
| 				return this.$root.api('signin', { | ||||
| 					username: this.username, | ||||
| 					password: this.password, | ||||
| 					signature: hexifyAB(credential.response.signature), | ||||
| 					authenticatorData: hexifyAB(credential.response.authenticatorData), | ||||
| 					clientDataJSON: hexifyAB(credential.response.clientDataJSON), | ||||
| 					signature: hexify(credential.response.signature), | ||||
| 					authenticatorData: hexify(credential.response.authenticatorData), | ||||
| 					clientDataJSON: hexify(credential.response.clientDataJSON), | ||||
| 					credentialId: credential.id, | ||||
| 					challengeId: this.challengeData.challengeId | ||||
| 				}); | ||||
|  |  | |||
|  | @ -22,12 +22,12 @@ | |||
| 
 | ||||
| 				<mk-switch v-model="usePasswordLessLogin" @change="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">{{ $t('passwordLessLogin') }}</mk-switch> | ||||
| 
 | ||||
| 				<mk-info warn v-if="registration && registration.error">{{ $t('something-went-wrong') }} {{ registration.error }}</mk-info> | ||||
| 				<mk-info warn v-if="registration && registration.error">{{ $t('error') }} {{ registration.error }}</mk-info> | ||||
| 				<mk-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('_2fa.registerKey') }}</mk-button> | ||||
| 
 | ||||
| 				<ol v-if="registration && !registration.error"> | ||||
| 					<li v-if="registration.stage >= 0"> | ||||
| 						{{ $t('activate-key') }} | ||||
| 						{{ $t('tapSecurityKey') }} | ||||
| 						<fa icon="spinner" pulse fixed-width v-if="registration.saving && registration.stage == 0" /> | ||||
| 					</li> | ||||
| 					<li v-if="registration.stage >= 1"> | ||||
|  | @ -67,16 +67,12 @@ import Vue from 'vue'; | |||
| import { faLock } from '@fortawesome/free-solid-svg-icons'; | ||||
| import i18n from '../../i18n'; | ||||
| import { hostname } from '../../config'; | ||||
| import { hexifyAB } from '../../scripts/2fa'; | ||||
| import { byteify, hexify, stringify } from '../../scripts/2fa'; | ||||
| import MkButton from '../../components/ui/button.vue'; | ||||
| import MkInfo from '../../components/ui/info.vue'; | ||||
| import MkInput from '../../components/ui/input.vue'; | ||||
| import MkSwitch from '../../components/ui/switch.vue'; | ||||
| 
 | ||||
| function stringifyAB(buffer) { | ||||
| 	return String.fromCharCode.apply(null, new Uint8Array(buffer)); | ||||
| } | ||||
| 
 | ||||
| export default Vue.extend({ | ||||
| 	i18n, | ||||
| 	components: { | ||||
|  | @ -157,8 +153,8 @@ export default Vue.extend({ | |||
| 				name: this.keyName, | ||||
| 				challengeId: this.registration.challengeId, | ||||
| 				// we convert each 16 bits to a string to serialise | ||||
| 				clientDataJSON: stringifyAB(this.registration.credential.response.clientDataJSON), | ||||
| 				attestationObject: hexifyAB(this.registration.credential.response.attestationObject) | ||||
| 				clientDataJSON: stringify(this.registration.credential.response.clientDataJSON), | ||||
| 				attestationObject: hexify(this.registration.credential.response.attestationObject) | ||||
| 			}).then(key => { | ||||
| 				this.registration = null; | ||||
| 				key.lastUsed = new Date(); | ||||
|  | @ -208,18 +204,13 @@ export default Vue.extend({ | |||
| 						challengeId: registration.challengeId, | ||||
| 						stage: 0, | ||||
| 						publicKeyOptions: { | ||||
| 							challenge: Uint8Array.from( | ||||
| 								atob(registration.challenge | ||||
| 									.replace(/\-/g, '+') | ||||
| 									.replace(/_/g, '/')), | ||||
| 								x => x.charCodeAt(0), | ||||
| 							), | ||||
| 							challenge: byteify(registration.challenge, 'base64'), | ||||
| 							rp: { | ||||
| 								id: hostname, | ||||
| 								name: 'Misskey' | ||||
| 							}, | ||||
| 							user: { | ||||
| 								id: Uint8Array.from(this.$store.state.i.id, c => c.charCodeAt(0)), | ||||
| 								id: byteify(this.$store.state.i.id, 'ascii'), | ||||
| 								name: this.$store.state.i.username, | ||||
| 								displayName: this.$store.state.i.name, | ||||
| 							}, | ||||
|  |  | |||
|  | @ -1,5 +1,33 @@ | |||
| export function hexifyAB(buffer) { | ||||
| 	return Array.from(new Uint8Array(buffer)) | ||||
| 		.map(item => item.toString(16).padStart(2, '0')) | ||||
| 		.join(''); | ||||
| export function byteify(data: string, encoding: 'ascii' | 'base64' | 'hex') { | ||||
| 	switch (encoding) { | ||||
| 		case 'ascii': | ||||
| 			return Uint8Array.from(data, c => c.charCodeAt(0)); | ||||
| 		case 'base64': | ||||
| 			return Uint8Array.from( | ||||
| 				atob( | ||||
| 					data | ||||
| 						.replace(/-/g, '+') | ||||
| 						.replace(/_/g, '/') | ||||
| 				), | ||||
| 				c => c.charCodeAt(0) | ||||
| 			); | ||||
| 		case 'hex': | ||||
| 			return new Uint8Array( | ||||
| 				data | ||||
| 					.match(/.{1,2}/g) | ||||
| 					.map(byte => parseInt(byte, 16)) | ||||
| 			); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| export function hexify(buffer: ArrayBuffer) { | ||||
| 	return Array.from(new Uint8Array(buffer)) | ||||
| 		.reduce( | ||||
| 			(str, byte) => str + byte.toString(16).padStart(2, '0'), | ||||
| 			'' | ||||
| 		); | ||||
| } | ||||
| 
 | ||||
| export function stringify(buffer: ArrayBuffer) { | ||||
| 	return String.fromCharCode(... new Uint8Array(buffer)); | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue