このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

Archives Details

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その3

Python

2020.06.14

この記事は最終更新日から1年以上が経過しています。

どもです。

前回の「Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その2」の続きとなります。

前回では、Django rest-authを用いて、JWTを用いてOAuth認証を実装いたしましたので、今回はフロント側の実装となります。

フロント側は、Nuxt.jsと認証モジュールにNuxt auth-moduleを利用します。

Nuxt auth-module Document

https://auth.nuxtjs.org/

今回は、SPAでのJWTを用いてOAuth認証の実装に関してなので、Nuxt.jsやauth modeuleの解説などは行わないのでご了承ください。

今回作成したソース

GitHub

https://github.com/webcyou-org/simple-rest-login-front

バージョン

  • TypeScript 3.9.5
  • Nuxt.js 2.12.2
  • @nuxtjs/auth 4.9.1
  • @nuxtjs/axios 5.3.6
  • bootstrap-vue 2.15.0

画面作成

早速画面を作成していきます。

1から作成もよいのですが、既存のライブラリ等も使いながらなるべく手を動かさず作成していきたいので、@nuxtjs/auth(auth-module)のデモページを流用しつつ、ちょっと手を加える形で作成していきます。

デモページ

https://nuxt-auth.herokuapp.com/login

GitHub

https://github.com/nuxt-community/auth-module/tree/dev/demo

auth-moduleレポジトリの「demo」ディレクトリが該当のソースとなります。

デモページを見ていただくと分かるのですが、以下のように、色々なOAuthログインに対応している形となっております。

ですが、今回はサーバー側でもOAuthログインは、「Google」のみの対応となっておりますので、それ以外の使用しない分は削除していきます。

また、topページも少し改修し、以下の様な表示にしました。

ログインページも簡素なものにします。

ユーザーネームログインとなっていたのを、メールアドレスによる認証と変更したいので、UIもそれに伴い修正しております。

ページ(page)改修の詳細説明は割愛させて頂き、重要な要点を絞った解説をさせて頂きますので、

詳細を知りたい方は、GitHubのソースを参照していただければと思います。

GitHub

https://github.com/webcyou-org/simple-rest-login-front/tree/master/pages

nuxt.config作成

それでは、nuxt.configを作成(改修)していきます。

まず、SPAで作成していきたいので、以下の様にmodeを「spa」に設定します。

nuxt.config.ts

mode: 'spa',

サーバー(Django Rest Framework (localhost:8000で立ち上げている))側にリクエストする際のエンドポイントを「/server」とし、proxyを設定します。実際には「/server」とリクエストされたものは、localhost:8000/に送信される形となります。これを行わないとNuxt.jsのaxios側でCORS(Cross-Origin Resource Sharing)でエラーとなりますので、その回避方法となります。

proxy: {
    '/server/': {
        target: 'http://localhost:8000/',
        pathRewrite: { '^/server/': '' }
    }
},

続いて、@nuxtjs/auth(auth-module)の設定となります。

auth: {
    cookie: false,
    redirect: {
        callback: '/callback',
        logout: '/signed-out',
        home: false
    },
...

まず、localStorageに保存する形にしたいので「cookie」をfalseとし、リダイレクト設定は「callback: ‘/callback’」「logout: ‘/signed-out’」「home: false」と設定します。

ここで、主に手を加えた点は「home: false」となります。

こちらは、ログイン後の遷移するページの指定となりますが、ここで設定を行ってしまうとOAuth認証フローのcallback時に、自動的に@nuxtjs/auth(auth-module)側でログインした形となってしまうのでそれを回避するためです。

callbackの際に、自動で遷移させず、サーバー(Django Rest Framework (localhost:8000で立ち上げている))側にリクエストし、その結果でログイン、ログイン失敗を判定させるためです。

ハイブリッドフローのポイントの箇所となります。

続いて、@nuxtjs/auth(auth-module)のstrategiesの設定となります。

strategies: {
    local: {
        tokenType: 'JWT',
        endpoints: {
            login: { url: '/server/rest-auth/login/', method: 'post', propertyName: 'token' },
            user: { url: '/server/rest-auth/user/', method: 'get', propertyName: '' }
        }
    },
    google: {
        client_id: 'YOUR_GOOGLE_CLIENT_ID',
        response_type: 'code token',
        scope: ['email', 'profile'],
        userinfo_endpoint: '/server/rest-auth/user/'
    }
}

まず、localですが、「メールアドレス」と「パスワード」でログインする時のAPIエンドポイントを設定しております。

ログイン時のエンドポイントと、メソッドプロパティ名となります。

propertyName: ‘token’」は、レスポンスのキーが、「token」で返却されますのでそれを受け取る為の指定となります。

endpointsuserは、ユーザープロフィールを取得するフローの際、リクエストのエンドポイント設定となります。

ログイン後、自動的にこちらのエンドポイントが叩かれるのと、SSR処理(ブラウザリロード)など走った時にも、ログインしている状態であれば自動的にリクエストする形となっております。

また、ここで、注意したい点が「tokenType: ‘JWT’」となります。

先程の「ユーザープロフィール取得」のAPIなど、自動的に取得するAPIはJWTトークンが、リクエストヘッダーのAuthorizationに付与されてリクエストされる形となります。

django-rest-auth側では「’JWT dqwfryth439rt34gninf’」と、「JWT」の文字列が先頭に付与された形で受け取る様になっているので、その形送信する為の指定となります。

最後にGoogleの設定ですが、Google Cloud Platformに登録された「client_id」の指定を行います。

google: {
    client_id: 'YOUR_GOOGLE_CLIENT_ID',
    response_type: 'code token',
    scope: ['email', 'profile'],
    userinfo_endpoint: '/server/rest-auth/user/'
}

response_typeは’code token’,と設定。

これによって、OAuth 2.0 Multiple Response Type の指定を行いハイブリッドフローを開始していることとなります。

scopeは取得したいデータを任意で設定します。

最後に「userinfo_endpoint」ですが、メールアドレス認証(local)同様、「ユーザープロフィール取得」のエンドポイントの指定となります。

メールアドレス認証と、キーは異なりますが上記のメールアドレス認証の際に説明した同様の処理の為の指定となります。

これで、nuxt.configの設定完了。

const config: Configuration = {
    mode: 'spa',
    build: {
        extractCSS: true
    },
    buildModules: [
        '@nuxt/typescript-build'
    ],
    modules: [
        'bootstrap-vue/nuxt',
        '@nuxtjs/axios',
        '@nuxtjs/auth'
    ],
    axios: {
        proxy: true
    },
    proxy: {
        '/server/': {
            target: 'http://localhost:8000/',
            pathRewrite: { '^/server/': '' }
        }
    },
    auth: {
        cookie: false,
        redirect: {
            callback: '/callback',
            logout: '/signed-out',
            home: false
        },
        strategies: {
            local: {
                tokenType: 'JWT',
                endpoints: {
                    login: { url: '/server/rest-auth/login/', method: 'post', propertyName: 'token' },
                    user: { url: '/server/rest-auth/user/', method: 'get', propertyName: '' }
                }
            },
            google: {
                client_id: 'YOUR_GOOGLE_CLIENT_ID',
                response_type: 'code token',
                scope: ['email', 'profile'],
                userinfo_endpoint: '/server/rest-auth/user/'
            }
        }
    }
}

callback

続いて「callback」の作成をしていきます。

「callback.vue」は、IDプロバイダの認可エンドポイントにリクエストし、表示されたログインページよりログイン後、「認可コード」と「アクセストークン」が返却され、表示するページとなっております。

、@nuxtjs/auth(auth-module)のデモでは、callback時に自動的にログイン後のページへと遷移しますが、今回、ハイブリッドフローの為「callback.vue」にて、返却された「認可コード」と「アクセストークン」を、サーバー(Django Rest Framework (localhost:8000で立ち上げている))側にリクエストします。

this.$auth.$state.strategyで、現在認証を行っているプロバイダ名が取得できますので、リクエスト成功した際に、レスポンスよりJWTトークンを受けとり@nuxtjs/auth(auth-module)の方に該当するプロバイダのJWTトークンをセットします。

user情報と、user tokenも同様に’JWT トークン’の形でセットします。

成功した際は、ログイン後表示する「secure」ページに遷移。

失敗の際は、一旦ログアウト処理を行い、ルートページに遷移させます。

created() {
    const provider = this.$auth.$state.strategy
    const callbackParams = queryString.parse(this.$auth.ctx.from.hash)

    this.$axios.post(`/server/rest-auth/${provider}/`, {
            access_token: callbackParams.access_token,
            code: callbackParams.code
        })
        .then((response) => {
            const data = response.data
            this.$auth.setToken(provider, data.token)
            this.$auth.setUser(data.user)
            this.$auth.setUserToken(`JWT ${data.token}`)
            this.$router.push('/secure')
        })
        .catch(() => {
            this.$auth.logout()
            this.$router.push('/')
        })
}

ログインテスト

それでは、一通り揃ったので、前回作成したユーザーでログインを行って行きたいと思います。

まずは、メールアドレスでの認証を試してみます。

ログイン成功し、認証済みのユーザーのみ表示するページの「secure」ページに遷移し、ログインしたユーザー情報が表示しているのが確認できます。

OAuth(google)でのログインも試してみます。

こちらは、リアルなアカウントを利用しているので画像は省略させていただきますが、メールアドレス認証と同様で問題なくログインでき「secure」ページに遷移し、ログインしたユーザー情報が表示しているのが確認できました。

ユーザー登録がされていない場合は、新規作成され、既にアカウントが存在する場合はログインとなります。

登録ページ作成

メールアドレスでのログイン、OAuth(Google)でのログインがそれぞれ確認できましたので、最後はユーザー登録できるように作成していきます。

ログインページの「login.vue」をコピーし、「registration.vue」を作成、以下の様に「Email」「Password」「PasswordConfirm」が入力できるように改修しました。

GitHub

https://github.com/webcyou-org/simple-rest-login-front/blob/master/pages/registration.vue

ユーザー登録のAPIエンドポイントは「/server/rest-auth/registration」となります。

こちらに、入力された「email」「password1」「password2」をパラメータとして送信します。

すべて必須の値となっており、漏れがあるとDjango側でエラーとなります。

this.$axios.post(`/server/rest-auth/registration/`, {
    email: this.email,
    password1: this.password,
    password2: this.passwordConfirm
})
.then(() => {
    this.$router.push('/login')
})
.catch((e) => {
    this.error = e.response.data
})

ヘッダーにも登録ページへの遷移を追加しました。

それでは、「fuga@fuga.com」のメールアドレスでアカウント作成

Django側のコンソールに登録後送信されるメールの内容が表示します。

アカウントアクティベート用のURLが表示し、アクセスするとアカウントがアクティベートされますが、今回特に制限入れていないので、アカウントアクティベートされていなくてもログインは行なえます。

ログインページで先程作成したアカウントでログインしてみます。

無事、ログイン成功し、認証済みのユーザーのみ表示するページの「secure」ページに遷移し、ログインしたユーザー情報が表示しているのが確認できます。

という訳で、簡易ではありますが、ユーザー登録、ログインが行えるようになりました。

アカウントアクティベートもそうですが、比較的簡易に作成したのですが、一通り作成完了しました。

最後に

その他にもパスワード変更やアクティベート処理などなどありますが、今回は割愛させて頂いたのと、エラー処理やJWTトークン期限設定、認証フローなど簡易にしてきたのもありますので、もし本番などで利用する際はその辺りの考慮や作り込みを行って頂ければと思います。

という訳で、3回に渡って書いてきた、「Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム」でした。

何か、参考になればと。

ではではぁ。

今回作成したソース

GitHub

https://github.com/webcyou-org/simple-rest-login-front

Comment

Related Article

【M1 Mac】Python ScrapyがImportErrorで大ハマリ。lxmlなど環境作成し対応した件。

2023.05.24

Django (DRF)で、ユーザーのモデルを作成時にハッシュidを別で保存する。

2020.09.10

macOS pyenv環境でtkinterが動かないので、再度インストール

2020.08.09

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その3

2020.06.14

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その2

2020.06.08

Django django-rest-auth + Nuxt.js auth-module で作る SPA JWT OAuth ログインシステム その1

2020.06.07

Django django-allauthで、サクッとソーシャルログイン機能を実装

2020.04.12

PythonでGUIアプリ開発「PyQt」を使った感想

2019.07.18

CATEGORY LIST

LATEST NEWS

Rust - Actix Web mongo ユーザー登録 JWT認証

Rust

2024.03.24

Rust - Actix Web JWT 認証認可 APIの作成

Rust

2024.02.25

Rust - Actix Web × JSON 静的ファイルをAPIで返却

Rust

2024.01.19

Rust - Actix Web × MongoDB環境をサクッと起動

Rust

2024.01.18

5分で学ぶ RustでWave Function Collapse (波動関数崩壊アルゴリズム)

Rust

2024.01.15

LLaMAモデル GGMLフォーマット(llama.cpp)をRustフレームワーク Leptosを用いて M1MacMiniでサクッと動かす。

Rust

2024.01.11

2024年 狙っているモバイルノートPC

tool

2024.01.07

MacOS XcodeにSDL2を追加

tool

2023.12.26

php 7.4にアップデート

PHP

2023.12.24

5分で覚える Flutter Flameで作る Wave Function Collapse - 波動関数崩壊アルゴリズム

AI・Bot・algorithm

2023.12.20

Flutter - Flameでゲーム作成 (キャラクターの移動)

Flutter

2023.07.23

Flutterで作る ChatGPT Prompt Manager

Flutter

2023.07.12

RANKING

Follow

SPONSOR

現在、掲載募集中です。



Links

About Us

WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。

Entry Profile

Graphical FrontEnd Engineer
- Daisuke Takayama

MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
現在、主に、ゲーム制作中心に港区六本木界隈で活動中。

FOLLOW US