Skip to content

Commit

Permalink
add totp api
Browse files Browse the repository at this point in the history
  • Loading branch information
narumincho committed Jan 13, 2024
1 parent 5564023 commit fd50ace
Show file tree
Hide file tree
Showing 13 changed files with 342 additions and 69 deletions.
1 change: 1 addition & 0 deletions note.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ fresh ではだめな理由...? 特殊な SSR が必要になるから CSS in JS
| path | 説明 | クエリパラメータ |
| ---------------- | --------------------------------------------- | ---------------- |
| / | トップページ 最近投稿されたアイデアなど | hl |
| /@{accountCode} | アカウントのページ | hl |
| /about | ライセンス, バージョン情報など | hl |
| /client | 表示言語, 端末のトークン情報など | hl |
| /file/{fileHash} | ファイルを配信 | |
Expand Down
160 changes: 160 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,30 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051
url: "https://pub.dev"
source: hosted
version: "64.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893"
url: "https://pub.dev"
source: hosted
version: "6.2.0"
args:
dependency: transitive
description:
name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.4.2"
async:
dependency: transitive
description:
Expand Down Expand Up @@ -41,6 +65,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
convert:
dependency: transitive
description:
name: convert
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
url: "https://pub.dev"
source: hosted
version: "3.1.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "40ae61a5d43feea6d24bd22c0537a6629db858963b99b4bc1c3db80676f32368"
url: "https://pub.dev"
source: hosted
version: "2.3.4"
fake_async:
dependency: transitive
description:
Expand All @@ -49,6 +97,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
fast_immutable_collections:
dependency: "direct main"
description:
name: fast_immutable_collections
sha256: "3eb1d7495c70598964add20e10666003fad6e855b108fe684ebcbf8ad0c8e120"
url: "https://pub.dev"
source: hosted
version: "9.2.0"
file:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
Expand All @@ -72,6 +136,30 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
http:
dependency: transitive
description:
name: http
sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139
url: "https://pub.dev"
source: hosted
version: "1.1.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
lints:
dependency: transitive
description:
Expand Down Expand Up @@ -104,6 +192,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
narumincho_json:
dependency: transitive
description:
name: narumincho_json
sha256: b3d9628f475af92b128f4cfc080104be10af5d781102814bf5f460470684274a
url: "https://pub.dev"
source: hosted
version: "0.1.0"
narumincho_util:
dependency: transitive
description:
name: narumincho_util
sha256: ece30f6faf5a46e87ded35389b77641e87687ce0093b421229aa945d548af450
url: "https://pub.dev"
source: hosted
version: "0.1.5"
package_config:
dependency: transitive
description:
name: package_config
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
path:
dependency: transitive
description:
Expand All @@ -120,6 +232,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
simple_dart_code_gen:
dependency: transitive
description:
name: simple_dart_code_gen
sha256: d200aed091c7392c62cc1d5fb99fed81ef343efdcd9162a49d0e34ade35d696b
url: "https://pub.dev"
source: hosted
version: "0.5.4"
simple_graphql_client_gen:
dependency: "direct main"
description:
name: simple_graphql_client_gen
sha256: edfcda02c54862e229763c4e52bca9ace2ffb8f1d23992b4d3cecb71c671734c
url: "https://pub.dev"
source: hosted
version: "0.7.8"
sky_engine:
dependency: transitive
description: flutter
Expand Down Expand Up @@ -173,6 +309,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
url_launcher:
dependency: "direct main"
description:
Expand Down Expand Up @@ -245,6 +389,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
watcher:
dependency: transitive
description:
name: watcher
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web:
dependency: transitive
description:
Expand All @@ -253,6 +405,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.3.0"
yaml:
dependency: transitive
description:
name: yaml
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.2.2 <4.0.0"
flutter: ">=3.16.0"
2 changes: 2 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ dependencies:
url_launcher: ^6.2.3
flutter_web_plugins:
sdk: flutter
fast_immutable_collections: ^9.2.0
simple_graphql_client_gen: ^0.7.8

dev_dependencies:
flutter_test:
Expand Down
61 changes: 45 additions & 16 deletions server/mutation/createAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,22 @@ import { accountIdFrom } from "../type/id.ts";
import { AccountCode } from "../type/accountCode.ts";
import { AccountDisplayName } from "../type/accountDisplayName.ts";
import { getAccountByCodeResolve } from "../query/accountByCode.ts";
import {TemporaryKeyId} from "../type/id.ts";
import {TotpKeyId} from "../type/id.ts";
import { CreateAccountDuplicateCode, CreateAccountNotFoundTotpKeyId } from "../type/createAccountResult.ts";
import { CreateAccountResult } from "../type/createAccountResult.ts";
import { TotpSecret } from "../type/totpSecret.ts";
import { TotpCode } from "../type/totpCode.ts";
import { TOTP } from "https://deno.land/x/[email protected]/totp.ts";

export const createAccount: g.GraphQLFieldConfig<
void,
Context,
{ readonly code: AccountCode; readonly displayName: AccountDisplayName }
{ readonly totpKeyId: TotpKeyId, readonly totpCode: TotpCode, readonly accountCode: AccountCode; readonly displayName: AccountDisplayName }
> = {
args: {
keyId: {
type: new g.GraphQLNonNull(TemporaryKeyId)
totpKeyId: {
type: new g.GraphQLNonNull(TotpKeyId),
description: "TOTPのキーID",
},
code: {
type: new g.GraphQLNonNull(AccountCode),
Expand All @@ -27,27 +33,50 @@ export const createAccount: g.GraphQLFieldConfig<
},
},
type: new g.GraphQLNonNull(Account),
resolve: async (_, args, { denoKv }): Promise<Account> => {
const existingAccountId = getAccountByCodeResolve({
code: args.code,
resolve: async (_, args, { denoKv }): Promise<CreateAccountResult> => {
const existingAccountId = await getAccountByCodeResolve({
accountCode: args.accountCode,
denoKv,
});
if(existingAccountId === null) {
return {
__typename: "CreateAccountDuplicateCode",
accountCode: args.accountCode,
};
}
const totpKey = (await denoKv.get<TotpSecret>(["temporaryTotpKey", args.totpKeyId])).value;
if(totpKey=== null) {
return {
__typename: "CreateAccountNotFoundTotpKeyId",
keyId: args.totpKeyId,

};
}
const isValidTotpCode = await TOTP.verifyTOTP(await TOTP.importKey(totpKey), args.totpCode);
if(!isValidTotpCode) {
return {
__typename: "CreateAccountInvalidCode",
accountCode: args.accountCode,
};
}
const displayName = AccountDisplayName.parseValue(
args.displayName || args.code,
args.displayName || args.accountCode,
);
const accountId = accountIdFrom(createRandomId());
const createDateTime = new Date();
await denoKv.set(["account", accountId], {
code: args.code,
await denoKv.atomic().delete(["temporaryTotpKey", args.totpKeyId]).set(["account", accountId], {
code: args.accountCode,
displayName,
createDateTime,
});
await denoKv.set(["cache", "accountByCode", args.code], accountId);
}).set(["cache", "accountByCode", args.accountCode], accountId).commit();
return {
id: accountId,
code: args.code,
displayName: displayName,
createDateTime,
__typename: "CreateAccountResultOk",
account: {
id: accountId,
code: args.accountCode,
displayName: displayName,
createDateTime,
}
};
},
description: "アカウントを作成する",
Expand Down
19 changes: 10 additions & 9 deletions server/mutation/createPreAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,27 @@ import { createRandomId } from "npm:@narumincho/[email protected]
import * as g from "npm:graphql";
import { Context } from "../context.ts";
import { TOTP } from "https://deno.land/x/[email protected]/mod.ts";
import { TemporaryKey } from "../type/tempKey.ts";
import { temporaryKeyIdFrom } from "../type/id.ts";
import { TotpKeyAndId } from "../type/totpKeyAndId.ts";
import { totpKeyIdIdFrom } from "../type/id.ts";
import { totpSecretFrom } from "../type/totpSecret.ts";

export const createPreAccount: g.GraphQLFieldConfig<
export const createTotpKey: g.GraphQLFieldConfig<
void,
Context,
{}
> = {
args: {},
type: new g.GraphQLNonNull(g.GraphQLString),
resolve: async (_, __, { denoKv }): Promise<TemporaryKey> => {
const key = await TOTP.exportKey(await TOTP.generateKey(32));
const id = temporaryKeyIdFrom(createRandomId());
await denoKv.set(["temporaryKey", id], key, { expireIn:
type: new g.GraphQLNonNull(TotpKeyAndId),
resolve: async (_, __, { denoKv }): Promise<TotpKeyAndId> => {
const key = totpSecretFrom(await TOTP.exportKey(await TOTP.generateKey(32)));
const id = totpKeyIdIdFrom(createRandomId());
await denoKv.set(["temporaryTotpKey", id], key, { expireIn:
// 30min
30 * 60 * 1000, });
return {
id,
secret: key,
};
},
description: "TOTPのキーを生成して",
description: "TOTPのキーを生成してデータベースに保存する",
};
6 changes: 3 additions & 3 deletions server/query/accountByCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const accountByCode: g.GraphQLFieldConfig<
type: Account,
resolve: async (_, args, { denoKv }): Promise<{ readonly id: AccountId }> => {
const accountId = await getAccountByCodeResolve({
code: args.code,
accountCode: args.code,
denoKv,
});
if (accountId === null) {
Expand All @@ -30,11 +30,11 @@ export const accountByCode: g.GraphQLFieldConfig<
};

export const getAccountByCodeResolve = async (
parameter: { readonly code: AccountCode; readonly denoKv: Deno.Kv },
parameter: { readonly accountCode: AccountCode; readonly denoKv: Deno.Kv },
): Promise<AccountId | null> => {
return (await parameter.denoKv.get<AccountId>([
"cache",
"accountByCode",
parameter.code,
parameter.accountCode,
])).value;
};
Loading

0 comments on commit fd50ace

Please sign in to comment.