DMM.comの、一番深くておもしろいトコロ。

VRでも簡単な認証でユーザーのVRMアバターをロードできる DMM VR Connect #2

VRでも簡単な認証でユーザーのVRMアバターをロードできる DMM VR Connect #2

  • このエントリーをはてなブックマークに追加

あらゆるVRアプリに好きな姿でダイブできるDMM VR Connect連載 #2

あらゆるVRアプリに好きな姿で飛び込みたい! そう思ったこと、一度はありますよね? そんな世界を実現するDMM VR ConnectとそのSDKについて、技術者目線で開発メンバーが全六回の連載でご紹介します。 二回目となる今回はアバターロードとDMM VR Connectの認証について、前回から引き続きあきらと、DMM VR Connect開発チームの鵜重がご紹介します。

前回はこちら

inside.dmm.com

目次

自己紹介

github.com DMM VR labチーフエンジニア兼コミュニティマネージャーのあきら(@sh_akira)です。
DVRSDKの開発リーダーを務めています。前回の#1に引き続き今回もよろしくお願いします。

github.com DMM VR labプラットフォームチーム所属の鵜重誠(@ainehanta)です。DMM VR Connectのフロントエンドおよびバックエンドを作ってVRアプリを支えています。2020年に新卒で入社して2年目になりました。今回の記事では「DMM VR Connectのイケてる認証システムの深いところまで話しちゃって!」ってことで、認証オタクの血がうずいています。

DMM VR ConnectはあらゆるVRアプリに好きな姿で飛び込めるサービス

www.youtube.com

VRアプリでアバター読み込んで動かすのって大変

f:id:sh_akira:20210421154137p:plain:h64 改めましてあきらです。私からはアバターロードについてお話します。

ひとことでアバター動かしたいとよく表現していますが、ちゃんと動かすには様々な実装が必要になります。

  • 3Dデータには多種多様な形式や構造、命名規則があり、それらを統一して同じ処理で動かさなければならない
    DVRSDKではVRMを採用して、この部分を考えずに済むようになっています。VRMとは
  • ファイルの管理をどうするか。QuestやiPhoneのようなデバイスでもVRMファイルを扱うには?
    DMM VR ConnectにVRMをアップロードしておけば、アプリはファイルの管理を考える必要がなくなる
  • 操作する人とアバターの大きさが違う状態で自身の体として上手く動かす必要がある
    →DVRAvatarには多様なアバターに対応したキャリブレーションが組み込まれています。詳しい仕組み
  • VRの場合はSteamVRやOculus、UnityXRといった環境ごとに実装が必要
  • フルトラッキングするためにはSteamVRやトラッカーの処理が必要
  • 指を動かすための制御にSteamVRのSkeletal InputやOculusでのボタンで自動制御が必要
  • アバターとして使うなら、表情の制御や自動でまばたきさせることも必要

DVRSDKにはこれら全ての機能が含まれています

でもDVRSDK使うとこんなに簡単!

DVRSDKを使ってDMM VR Connectからアバターをロードするまでの流れを2ステップでご紹介します。

ステップ1 アプリからConnectに接続するためのクライアントIDを発行する

以下は、ログインしたらクライアント登録してクライアントIDを一つ発行する手順です。

f:id:sh_akira:20210420181247p:plain
DMM VR Connect for Devs
1. DMM VR Connect for Devsで新規登録 / ログインをします。
f:id:sh_akira:20210420181818p:plain
新規登録画面
2. もしConnectに初めて登録する場合はユーザー登録が必要なので、画面に従って新規登録します。既に登録済みの場合は5に進みます
f:id:sh_akira:20210420182124p:plain
ユーザー登録画面
3. 必要事項を入力して進みます
f:id:sh_akira:20210420182601p:plain
アバター登録画面
4. アバターの追加画面が表示されるので+ボタンを押してVRMモデルをアップロードしておきます。
f:id:sh_akira:20210420193935p:plain
クライアント登録画面
5. for Devsのページでログインするとクライアントの追加画面が開くので+ボタンを押してクライアントを追加します。
f:id:sh_akira:20210420194106p:plain
クライアント新規作成画面
6. クライアント名を入力します。もしアプリ名が決まっていたらそれを入力します。後から変更可能なので、まずは何か入力して保存します。
f:id:sh_akira:20210420195500p:plain
クライアントIDの確認
7. クライアントIDが生成されるのでコピーしておきます。1アプリに1クライアントIDが必要です。このIDは他者に知られないように注意してください。

ステップ2 UnityでSDKを使う

今回はUnity 2019.4.24f1で説明します。

  1. Unity 2019.4.24f1で新規プロジェクトを作成します。
  2. 必要なパッケージを事前にインポートします
    UniVRM 0.62.0
    ・その他サンプルごとに必要なパッケージはDVRSDKのGitHubにあるReadmeをご覧ください。
    f:id:sh_akira:20210421042313p:plain
    DVRSDKのインポート
  3. 最新のDVRSDKをダウンロードしてインポートします。画像のように不要なもののチェックを外してインポートします。 チェックを外したものを別途試したい場合はそれぞれ別に必要なパッケージがあるのでReadmeをご覧ください。
    f:id:sh_akira:20210421042608p:plain
    SdkSettingsの作成
  4. Assets/Resourcesフォルダを作成し、メニューのCreate->DVRSDK->Create Configurationで生成されたファイル名をSdkSettingsにしたら、Client_idに発行したクライアントIDを入力します。
    f:id:sh_akira:20210421043506p:plain
    2DUIExampleのサンプルシーン
  5. Assets/DVRSDK/Examples/2DUIExample/Scene/2DUIExampleシーンを開いて実行してみましょう。ログインしてアバター読み込みまでの一連の流れが全て体験できます。

これだけで導入完了です。

詳細についてはサンプルの中身を見ていただければと思いますが、自分で呼び出しのコードを書く場合どうなるかというと

void Authorize()
{
    //初期化を行う
    var sdkSettings = Resources.Load<SdkSettings>("SdkSettings");
    var client_id = sdkSettings.client_id;
    var config = new DVRAuthConfiguration(client_id, new UnitySettingStore(), new UniWebRequest(), new NewtonsoftJsonSerializer());
    Authentication.Instance.Init(config);

    //DMM VR Connectに接続して認証する
    Authentication.Instance.Authorize(
        //ブラウザで認証が必要な場合
        openBrowser: response => Application.OpenURL(response.VerificationUri),
        //認証に成功した場合
        onAuthSuccess: OnAuthSuccess,
        //認証に失敗した場合
        onAuthError: exception => Debug.LogError(exception)
    );
}

async void OnAuthSuccess(bool isSuccess)
{
    if (isSuccess)
    {
        //VRMローダー初期化
        var vrmLoader = new VRMLoader();
        //カレントユーザーを取得
        var currentUser = await Authentication.Instance.Okami.GetCurrentUserAsync();
        //アバターをダウンロードしてGameObjectを取得
        var currentModel = await Authentication.Instance.Okami.LoadAvatarVRMAsync(currentUser.current_avatar, vrmLoader.LoadVRMModelFromConnect) as GameObject;
        //ロードしたVRMを表示する
        vrmLoader.ShowMeshes();
    }
}

これだけでDMM VR Connectに認証を行い、シーン上にVRMのアバターが表示されます。

さらにOculusVRExampleやSteamVRExampleをインポートすると、この読み込みに加えてVR機器の自動処理やキャリブレーションまでできるサンプルが使えます。

VRアプリのユーザー認証は大事だけど大変

f:id:ainehanta:20210421153934p:plain:h64ここからは鵜重のターン。私からは認証についてお話します。

f:id:ainehanta:20210421153930p:plain:h64ちなみにこのロボはConnect Chatの公式アバターのGino(ジーノ)です。

あきらさんの説明のとおり、DVRSDKを使うと簡単に VRMアバターをDMM VR Connectからロードして、キャリブレーションして、動かすことができるようになります。

でも、VRアプリでアバターを使うためにはこれだけでは不十分です。なぜならば、VRアプリでは アバター=自分という認識がとても重要だからです。自分以外の人物が自分に成り済ましてアバターを使用していたら大問題ですよね。

そこで、アバターを利用するVRアプリでは、ユーザー本人であることを確認する認証を必ず実装しなければなりません。

f:id:ainehanta:20210421125927p:plain
アバターは自分のアイデンティティなのでしっかり認証しなければならない

一般的なユーザーアカウントの認証の場合、ログイン画面を開いてIDやパスワードを入力して認証を行います。

でもVRって通常HMD被ってますよね。ってことは、HMD脱ぐ → 物理キーボードでID・パスワード打つ → HMDかぶる or 仮想キーボードで頑張る(大文字小文字数字を混ぜて8文字以上で…)。

f:id:ainehanta:20210420144740g:plain
仮想キーボードでID・パスワードを入力するのが辛いの図

辛い。辛いですね。そうなんです。VRと文字入力って相性悪すぎるんです。仮想キーボードで入力していると指がムズムズしちゃいます。

僕ならコンテンツに到着する前にそっとじします。でも、認証重要なのにどうしたらいいんでしょう?

DMM VR ConnectとDVRSDKはデバイスフローで体験を邪魔しない認証を提供!

そんなあなたにDMM VR ConnectDVRSDK! なんと、VRアプリとスマホやPCのブラウザを連携させてDMM VR Connectにログインできます。スマホならギリHMDの隙間から見えるのでHMDつけたままでもID・パスワード入力もへっちゃらです!

f:id:ainehanta:20210421124528j:plain:h300
スマホならHMDの隙間からギリ見える

実際にDVRSDKDMM VR Connectに楽々ログインしてみましょう。

f:id:ainehanta:20210420154913g:plain
DVRSDKで楽々ログイン

  1. DVRSDKを導入しているアプリではログインボタンを押すとVRアプリに認証ページのURLとアルファベットのコードが表示されます。
  2. 手元のスマホで認証ページを開き、コードを入力すると通常のログイン画面が表示されます。
  3. そして、IDやパスワードを入力しスマホ側でログインを完了するとVRアプリ側も自動的にログインされます

この魔法のような認証は、RFC8628 OAuth2.0 Device Authorization Grant(以下デバイスフロー)という標準規格によって実現されています。

デバイスフローの解説に入る前に、いくつか認証に関連する分かりにくいところを説明しておきましょう。

事前知識 認証は誰であるかを確認すること、認可は何をして良いか許可すること

まず認証について話す時必ず通る道、認証(Authentication)と認可(Authorization)の話をしなければなりません。

認証と認可って混ざりますよね。どっちも英語にすると長いしぱっと見同じに見えるし。でも、違うんです。

認証(Authentication)は誰であるかを確認することです。

例えばパスワードなどの本人しか知らない情報(知識要素)を答えることができれば本人であると言えます。他にもIDカードを持っているかなどの所有要素や指紋などの生体要素を使って本人確認することもあります。

f:id:ainehanta:20210421125924p:plain
認証(Authentication)は誰であるかを確認すること

認可(Authorization)はある条件をもとに何をして良いか許可することです。

例えば、人事部に所属している人には勤怠情報の閲覧を許可する。鍵を持っている人には通行を許可するなどの例が分かりやすいと思います。

ここで重要なのは「人事部に所属している」という条件をもとに判断しているということです。「人事部の山田さん」のように誰が操作しようとしているのかは関係ありません。ただし、認可の条件に、認証で確認した特定の人物であることを使うこともできます。このあたりが認証と認可の紛らわしいところですね。

f:id:ainehanta:20210421130642p:plain
認可(Authorization)はある条件をもとに何をして良いか許可すること

DMM VR Connectでは誰が使用するのかを認証し、その人が操作するVRアプリにアバターをロードすることを認可しています。

f:id:ainehanta:20210420163150p:plain:h300
DMM VR Connectの認可画面

事前知識 OAuthは認可、OpenID Connectは認証をする規格

さらっとOAuth2.0 Device Authorization Grant(デバイスフロー)で認証を実現していると書いていますが、OAuthって謎ですよね。

OAuthとはサービス間で安全に認可を行うための規格です。

例えばブログ記事を書いたことをSNSに投稿したい時、ブログシステムにSNSのIDとパスワードを保存して投稿してもらうということも可能です。

しかし、ブログシステムから何かしらの要因でデータベースの内容が漏れてしまったら一大事ですね。

そこでOAuthを使うことで、ユーザーにブログシステムからSNSに投稿することの許可をもらい、その証としてID・パスワードではない一時的なトークンを保存・使用してSNSに投稿できるようにします。

f:id:ainehanta:20210421142636p:plain
OAuthはIDやパスワードを拡散させずにアプリ間連携が行える

DMM VR Connectでも採用しているデバイスフローはOAuth2.0の中の認可方法の1つです。なので、厳密に言えば以下の文は間違っています。OAuthで認証はしていません。

さらっとOAuth2.0 Device Authorization Grant(デバイスフロー)で認証を実現していると書いていますが…

では、なぜDMM VR Connectではデバイスフローで認証ができるのでしょうか? それはOpenID ConnectというOAuthを拡張し、認証もできるようにした規格を使っているからです。OpenID Connectについてもたっぷり話したいところですが長いのでスキップしましょう。

したがって、正しくは、DMM VR ConnectはデバイスフローとOpenID Connectを使って認証を提供していると書くべきでしょう。

デバイスフローって何? どうやって動いてるの?

さあ、いよいよデバイスフローが何者なのかについて説明していきましょう。

デバイスフローは冒頭でも紹介したとおりRFC8628 OAuth2.0 Device Authorization Grantが正式名称で、OAuth2.0の拡張として策定されています。

デバイスフローはその名のとおり、IoTデバイスやスマートTVなどWebブラウザや文字入力に制限のあるデバイスでOAuthを使用するために策定されました。

まさに文字入力が大変なVRアプリにうってつけなわけです。

メジャーどころだとYouTube VRのアプリがデバイスフローを使ってますね。

f:id:ainehanta:20210420183149j:plain:h300
デバイスフローの例

デバイスフローはWebフロントエンドとHTTPベースのWeb APIで実装されています。デバイスフローのシーケンスはこんな感じです。

f:id:ainehanta:20210421145338p:plain
デバイスフローの簡易版シーケンス図

それでは、デバイスフローでアクセストークンを取得する流れを簡単に追っていきましょう。

ステップ1 アプリがデバイス認可リクエストを飛ばして各種コードを発行する

f:id:ainehanta:20210421132431p:plain
認可リクエストを飛ばしてユーザーコードとURLを表示する

レスポンスとして以下の情報を得るので、アプリで表示します。

  • ユーザーにブラウザで開いてもらうURL
  • ユーザーがブラウザに入力するワンタイムのコード(ABCD-EFGHみたいなやつ)→ユーザーコード
  • アプリがトークンを引き換えるのに使うコード→デバイスコード
  • (あれば)ポーリングの間隔
  • (あれば)ユーザーコード入力済みのURL

ちなみに、ユーザーコード入力済みのURLを使えば、ユーザーの入力を一手間減らせます。VRではないアプリでDVRSDKを利用する場合、直接アプリがブラウザを立ち上げる時にこのURLを使用しています。

ステップ2.a ユーザーがブラウザでログイン画面を開いてログイン

f:id:ainehanta:20210421150329p:plain
ブラウザでログインする流れ

開いたページにユーザーコードを入力し、ログインすると、ユーザーコードに紐付くデバイスコードにログイン済みフラグが立ちます。

ステップ2.b その間、アプリはトークンをポーリングする

ユーザーのログインを待っている間、アプリはデバイスコードを送信してアクセストークンをポーリングします。

ステップ3 トークンを取得して完了

f:id:ainehanta:20210421132649p:plain
ログイン完了するとアプリ側にアクセストークンが渡される

アクセストークンを取得して完了です。

DVRAuthは何をしているの?

前述のとおり、デバイスフローはポーリングなど少しややこしい実装が必要です。もちろんDVRSDKの認証モジュールのDVRAuthはたった3メソッドの呼び出しで実装できます。

TIPS

ちなみにDVRAuthはUnityではないC#でも実行することができます。これはデバッグなどで助かるんですが、PC以外のUnityのC#でdotnetのHttpClientを使用すると証明書エラーになります。

DVRSDKではUnityWebRequestとHttpClientを抽象化して切り替えるようになっているので問題なく使えます。ユーティリティアプリを作成する時などにも便利ですね。

DVRAuthで自動ログイン

もう一つDVRAuthの大事な機能としてログイン状態の維持があります。DMM VR Connectで取得できるアクセストークンには有効期限があります。有効期限が切れると再度ログインしてアクセストークンを取ってくる必要があります。

でも、アプリを使っている時に頻繁に再ログインを求められたら嫌ですよね?

大丈夫、安心してください。DVRSDKの認証をしているモジュールのDVRAuthではこのあたりもカバーしています。

実はアクセストークンを取得した時に一緒に、以後アクセストークンをユーザーの認証なしに取得できる特別なトークンを一緒に取得しています。

それはリフレッシュトークンと呼ばれるもので、DVRAuthではそのリフレッシュトークンを暗号化して安全に保存しています。アクセストークンの期限が切れると自動的にリフレッシュトークンを復号してアクセストークンを更新してくれちゃいます。

TIPS

暗号化には共通鍵暗号を使用し、その鍵を公開鍵暗号でさらに暗号化して保存しています。公開鍵暗号の秘密鍵はOSが持っているキーストアに保存しています。

ここでもAndroidとPCではキーストアの実装が異なりますが、DVRSDKで抽象化して切り替えています。安心してAndroidでもPCでも安心してDVRAuthを使えますね。

また、ログイン維持機能を応用するとアプリ起動時にリフレッシュトークンが保存されている=すでにログインしているとみなして自動ログインする機能を実現できます。

それもたったの1メソッドで!

public async void LoginTest()
{
    var sdkSettings = Resources.Load<SdkSettings>("SdkSettings");
    var client_id = sdkSettings.client_id;
    var config = new DVRAuthConfiguration(client_id, new UnitySettingStore(), new UniWebRequest(), new NewtonsoftJsonSerializer());
    Authentication.Instance.Init(config);
    var canAutoLogin = await Authentication.Instance.TryAutoLogin(onAuthSuccess: async isSuccess =>
    {
        if (isSuccess)
        {
            if (await GetMyVRM())
            {
                SetLog("VRM Loaded");
            }
            else
            {
                SetLog("VRM Load Failed");
            }
        }
        else
        {
            SetLog("Login Failed");
        }
    });
}

ところでDMM VR Connectはどうやって認証を提供しているの?

と、クライアントの実装が分かったところで、サーバーサイドはどうやって実装しているのか気になると思います。

実はDMM VR Connectでは認証システムをAuth0というサービスを利用して提供しています。

auth0.com

DMM insideにもAuth0をトライアルした記事があったのでAuth0の概要はそちらをご覧ください。

inside.dmm.com

ここでは、なぜDMM VR ConnectがAuth0を採用したのかを中心に書いていこうと思います。

Auth0を採用した理由その1 デバイスフローに対応

これは言わずもがな。Auth0がデバイスフローに標準対応しています。これだけで狂喜乱舞です。

Auth0を採用した理由その2 めっちゃ柔軟だけど標準規格ばっちり

Auth0はルールという、認証時にJSのスクリプトを走らせる機能があります。これを使うと好きなようにAuth0の動作をカスタマイズできちゃいます。

DMM VR ConnectではGoogleアカウントやFacebookアカウントを利用してログインしていただいています。しかしVRは現実のアイデンティティとは別なことも多いので、むしろ名前とかは消えていてほしいですよね。

安心してください。DMM VR ConnectのAuth0ではルールで匿名化してあります。

f:id:ainehanta:20210421131915p:plain
ルールで動作をカスタマイズできるのは良い(例:匿名化)

そんな柔軟なAuth0ですが、認証・認可部分はOAuthやOpenID Connectなどの標準規格に準拠しているので信頼できるし、VR labのR&Dの側面として標準規格で実装した知見って重要です。

あと標準規格だと仕様が公開されているので、ライブラリの実装しやすいのもポイントです。

セキュリティ要件も高いし、認証基盤って作るの大変なんですよ。それがUnity用のライブラリ作るだけで済むってのはありがたいです。 普通に作ってたらDMM VR Connectのリリースは一年先になっていたことでしょう。

Auth0を採用した理由その3 サンプルとかライブラリが豊富

言語問わずメジャーなフレームワークのサンプルやライブラリが用意されているのは圧巻です。DMM VR ConnectのフロントエンドはReact、バックエンドはExpressで作成されていますが、Auth0のライブラリでさくっと実装できています。

まとめ

DMM VR ConnectDVRSDKを使えば、ややこしいVRアプリでの認証を簡単に実装できて、VRMアバターもロードできて動かせちゃう!すごい!!

認証って影が薄めですがVRアプリやアバターを使う時の重要さとDMM VR Connectの認証へのこだわりを感じていただけたら幸いです。

ぜひ皆さんもデベロッパー登録してアプリを作ってみてくださいね。 https://bit.ly/3aDpRJp

もし実装で困った時は、DMM VR lab公式DiscordサーバーのDMM VR lab Communityで気軽にお問い合わせください! discord.gg