60 min
Saito
  1. 1. 概要
    1. 1.1. 材料
    2. 1.2. 機能
  2. 2. アプリの作成
    1. 2.1. Frontend
    2. 2.2. Backend
  3. 3. アプリのカスタマイズ
    1. 3.1. Frontend
    2. 3.2. Backend
  4. 4. アプリのデプロイ
    1. 4.1. Database
    2. 4.2. Backend
    3. 4.3. Frontend
  5. 5. ダウンロード
  6. 6. Version

人気の認証サービスAuth0を使って, K5 Playgroundで作成したチャットアプリに認証機能を追加します。

概要

材料

  • テンプレート: Chat
  • APIロジック: MongoDB, Auth0 Validate JWT
  • サービス: MongoDB

機能

  • チャットアプリ
  • Auth0による認証機能とログインUI
  • WebAPIをJWTで保護

アプリの作成

K5 Playgroundにアクセスします。

Frontend

  1. Chatテンプレートを選択します。

Backend

  1. POST /sample_streamsを選択して、AuthenticationメニューのAuth0 Validate JWTを先頭に追加します。
  2. POST /sample_messagesを選択して、AuthenticationメニューのAuth0 Validate JWTを先頭に追加します。
  3. アプリケーションをダウンロードします。

アプリのカスタマイズ

Frontend

  1. auth0-js moduleをインストールします。

    1
    npm install --save auth0-js
  2. Auth0の認証モジュールを作成します。

    詳細はAuth0公式サイトのReact用サンプル Sample Projectを参照してください。

    1. app/utils/Auth/auth0-variables.jsを新規作成して、情報を入力します。

      1
      2
      3
      4
      5
      6
      export const AUTH_CONFIG = {
      domain: '[Domain]',
      clientId: '[Client ID]',
      callbackUrl: '[Callback URL]',
      audience: '[API Identifier]',
      };
    2. Auth0の認証モジュールapp/utils/Auth/Auth.jsを新規作成します。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      import auth0 from 'auth0-js';
      import { AUTH_CONFIG } from './auth0-variables';

      export default class Auth {
      auth0 = new auth0.WebAuth({
      domain: AUTH_CONFIG.domain,
      clientID: AUTH_CONFIG.clientId,
      redirectUri: AUTH_CONFIG.callbackUrl,
      audience: AUTH_CONFIG.audience,
      responseType: 'token id_token',
      });

      constructor() {
      this.login = this.login.bind(this);
      this.logout = this.logout.bind(this);
      this.handleAuthentication = this.handleAuthentication.bind(this);
      this.isAuthenticated = this.isAuthenticated.bind(this);
      }

      login() {
      this.auth0.authorize();
      }

      handleAuthentication() {
      const authResult = location.hash.substr(1).split('&').reduce(
      (authObject, hash) => Object.assign(
      {},
      authObject,
      { [hash.split('=')[0]]: hash.split('=')[1] },
      ),
      {},
      );

      if (authResult && authResult.access_token && authResult.id_token) {
      this.setSession(authResult);
      location.href = '/';
      } else if (authResult.error) {
      alert(`Error: ${authResult.error}.`);
      location.href = '/';
      }
      }

      setSession(authResult) {
      const expiresAt = JSON.stringify((authResult.expires_in * 1000) + new Date().getTime());
      localStorage.setItem('access_token', authResult.access_token);
      localStorage.setItem('id_token', authResult.id_token);
      localStorage.setItem('expires_at', expiresAt);
      // navigate to the home route
      location.href = '/';
      }

      logout() {
      localStorage.removeItem('access_token');
      localStorage.removeItem('id_token');
      localStorage.removeItem('expires_at');
      location.href = '/';
      }

      isAuthenticated() {
      const expiresAt = JSON.parse(localStorage.getItem('expires_at'));
      return new Date().getTime() < expiresAt;
      }
      }
  3. ログイン/ログアウトのボタンを追加します。

    1. app/components/StreamList/StreamList.jsを編集します。

      1
      2
      3
      import { RaisedButton002 } from 'material-ui-fj/Button';
      import Auth from '../../utils/Auth/Auth';
      const auth = new Auth();

      ログイン/ログアウトボタンを追加

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
        />
      <RaisedButton002
      label="Add New Stream"
      onClick={this.handleCreateStream}
      tabIndex={0}
      />
      <RaisedButton002
      label={auth.isAuthenticated() ? 'Logout' : 'Login'}
      onClick={this.handleAuthentication}
      style={{ marginTop: '2px' }}
      tabIndex={0}
      />
      </VerticalLayout>

      ログイン/ログアウトのイベントハンドラ

      1
      2
      3
      4
      5
      6
      7
      8
      9
      handleAuthentication() {
      if (auth.isAuthenticated()) {
      auth.logout();
      } else {
      auth.login();
      }
      }

      render() {

      アクセストークンの処理

      1
      2
      3
      4
      5
      6
      7
      componentWillReceiveProps(nextProps) {
      if (/access_token|id_token|error/.test(nextProps.location.hash)) {
      auth.handleAuthentication();
      }
      }

      componentWillUnmount() {
  4. ログアウト状態時に、POSTボタンやテキスト入力フィールドを無効化させます。

    app/components/StreamContent/StreamContent.jsを次のように修正します。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
      <input
    type="text"
    ref={(component) => { this.userId = component; }}
    placeholder={'Name'}
    style={styles.userId}
    tabIndex={0}
    disabled={!auth.isAuthenticated()}
    />
    <textarea
    ref={(component) => { this.message = component; }}
    placeholder={'Message'}
    style={styles.message}
    tabIndex={0}
    onKeyDown={
    event => this.handleEnterMessage(event, currentStream._id, replyMessageId)
    }
    disabled={!auth.isAuthenticated()}
    />
    </VerticalLayout>
    <VerticalLayout style={styles.entrySubmit}>
    <BorderedFlatButton002
    label="post"
    onClick={() => this.handleSubmitMessage(currentStream._id, replyMessageId)}
    tabIndex={0}
    disabled={!auth.isAuthenticated()}
    />

    app/actions/StreamActionCreators.jsを修正します。

    1
    2
    3
    4
    5
    6
    createStream({ name, resolve }) {
    api.post('/sample_streams', { name }, {
    headers: {
    Authorization: localStorage.access_token ? `Bearer ${localStorage.access_token}` : undefined,
    },
    }).then((response) => {

    app/actions/MessageActionCreators.jsを修正します。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    sendMessage({ streamId, userId, message, replyMessageId, resolve }) {
    api.post('/sample_messages', {
    stream_id: streamId,
    user_id: userId,
    message,
    replyMessage_id: replyMessageId,
    }, {
    headers: {
    Authorization: localStorage.access_token ? `Bearer ${localStorage.access_token}` : undefined,
    },
    }).then(() => {

Backend

config.jsにMongoDBやAuth0の接続情報を記載してください。

アプリのデプロイ

Database

MongoDBに次のコレクションを作成します。

  • sample_message
  • sample_stream

Backend

1
2
cd backend
cf push [APP_NAME]

Frontend

1
2
3
4
5
6
cd frontend
npm install
set API_URL=[Backend URL] # for Windows
export API_URL=[Backend URL] # for Mac/Linux
npm run build
cf push [APP_NAME] -p public

ダウンロード

Version

  • Chat Template: v1.0.0