EduTalk

教育に関するテクノロジーやスタートアップ、考えたことについて発信中。

Rails APIとEmber CLIでユーザー登録、ログインを実装する - パート2

f:id:atsuhio:20150411183835j:plain
このシリーズではRailsAPIとEmber CLIを利用して、ユーザー登録、ログインを実装していきます。全部で4つのパートで構成される予定です。
Part1はこちら

前回の記事で、RailsAPIとEmber CLIのそれぞれを使ったアプリを作成し、Userを作成するところまで行いました。
今回は、Ember CLI側の実装を行っていきます。

まず最初に、Configで今回のエンドポイントである"http://api.auth-test-web.dev/v1"を設定します。
environment.jsのif (environment === 'development') { の後に設定を書きます。

/api-test-web/config/environment.js/
  if (environment === 'development') {
    ENV.namespace = 'v1',
    ENV.host = 'http://api.auth-test-api.dev'
  }

次にApplicationAdapterを作り、そこでhostとnamespaceを登録します。
また、今回Rails側でActiveModelSerializerを利用しているため、ActiveModelAdapterをbaseのadapterとして設定します。

ember g adapter application


/api-test-web/app/adapters/environment.js/
import DS from 'ember-data';
import config from 'auth-test-web/config/environment';

export default DS.ActiveModelAdapter.extend({
  namespace: config.namespace,
  host: config.host
});

次に、UserModelを作成していきます。
パスワード情報はEmber側で保存したくないので、Attributeはnameとemailのみです。

ember g model user


import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr(),
  email: DS.attr()
});

次にRouteを編集していきます。

ember g route index
ember g route login
ember g route signup
ember g route secret

secretページはログインされてない状態ではアクセス出来ないページです。

コントローラーも作成します。

ember g controller login
ember g controller signup
ember g controller secret

次に、テンプレートを作っていきます。

auth-test-web/templates/index.hbs
<p>Index</p>
<ul>
  <li>{{#link-to 'signup'}}ユーザー登録{{/link-to}}</li>
  <li>{{#link-to 'login'}}ログイン{{/link-to}}</li>
  <li>{{#link-to 'secret'}}秘密のページ{{/link-to}}</li>
</ul>


auth-test-web/templates/login.hbs
<form method="post" {{action "login" on="submit"}}>
  {{input value=email type="text" class="email" placeholder="email"}}
  {{input value=password type="password" class="password" placeholder="password"}}
  {{input value="Login" type="submit"}}
</form>


auth-test-web/templates/secret.hbs
<p>秘密のページへようこそ。◯◯さん</p>

Secretページには、最終的にログインしているユーザーの名前、signoutのリンクを追加していきます。

実際にユーザー認証(Authentication)を実装していきます。

今回、Ember.Serviceクラスを拡張したAuthServiceでユーザーの認証全体を管理していくため、まずはサービスを作ります。

ember g service auth

以下、簡単にユーザー認証の流れを書きます。

  1. サイトへの各訪問者は、初期状態で"logged-out"という状態にあるようにします。
  2. Loginテンプレートで、フォーム入力を行い"login"ボタンを押します。
  3. Loginコントローラーで、loginアクションが呼び出され、フォームのデータを引数にAuthサービス内のAuth.login(data)を呼び出します。
  4. Auth.loginは、AjaxリクエストをAPIサーバーに送ります。
  5. 正しいEmail、パスワードが入力されていた場合、ステータス200と共にauthToken、userIdの情報が帰ってきます。間違った入力を行った場合にはステータス401が帰ってきます。
  6. 200が帰ってきた場合、Auth側のauthToken、userIdの情報を登録し、stateを"logged-in"へと変更します。


この流れを一つ一つ実装していきます。
まずはAuthサービス内でinit関数を作ります。またログイン、ログアウト、ログイン中の3つの状態にアクセスしやすいよう"loggedIn", "loggedOut", "loggingIn"の3つのcomputed propertyを登録します。

auth-test-web/app/services/auth.js
import Ember from 'ember';
import config from 'auth-test-web/config/environment';

export default Ember.Service.extend({
  init: function() {
    this._super();
    this.setProperties({
      authToken: localStorage.getItem('authToken'),
      userId: localStorage.getItem('userId'),
      serverTokenEndpoint: config.host + '/' + config.namespace,
      state: localStorage.getItem('authToken') ? "logged-in" : "logged-out"
    }}
  },
  loggedIn: function() {
    return this.get("state") === 'logged-in';
  }.property('state'),

  loggedOut: function() {
    return this.get("state") === 'logged-out';
  }.property('state'),
  LoggingIn: function() {
    return this.get("state") === "logging-in";
  }.property('state')
  ....
}

init内では、ページがリロードされた際にauthToken、userId、stateプロパティの情報をlocalStorageから取り出します。localStorageに情報を保存していない場合、ページがリロードされた際に情報がなくなってしまうため、その度に毎回ユーザーがログインしなければいけなくなってしまいます。


実際に、この状態が反映されるかどうか確認してみます。

Dependency Injection

そのままだと、Auth内の情報にEmberアプリから直接アクセス出来ないため、AuthInitializerを作成します。

ember g initializer auth


import Auth from 'auth-test-web/services/auth';

export function initialize(container, app) {
  app.register('auth:main', Auth);

  app.inject('route', 'auth', 'auth:main');
  app.inject('controller', 'auth', 'auth:main');
  app.inject('application', 'auth', 'auth:main');

  app.inject('auth', 'store', 'store:main');
}

export default {
  name: 'auth',
  after: 'ember-data',
  initialize: initialize
};

次にindex.hbsに次を追記します。

{{#if auth.loggedIn}}
ログインしていません。
{{/if}}

indexページを開いてみます。次のように表示されていたらOKです。

f:id:atsuhio:20150411135507p:plain

次回、パート3では実際にloginアクションを実装していきます。
パート3はこちら

リンガルボックスでは現在CTOを募集しています。

リンガルボックスでは、オンライン英会話スクール「リンガルボックス」を運営しています。既にオンライン英会話では上場しているレアジョブに、DMM英会話、ラングリッチなど多数の会社がありますが、レアジョブの会員が3万人ほどで、日本だけでも語学学校に通う生徒数全体の中(500万人と言われています。)ではまだ小さな割合しか占めていないこと、まだ開拓の全く進んでいないブラジルやロシア、ヨーロッパなどの市場があること、オンライン英会話のサービス内容自体にもイノベーション余地があることなど、まだまだ可能性があると考えています。しかし、リンガルボックスの考えるビジョンを実現していくためには高い技術力が不可欠です。(こんなチュートリアルを書いていますが、代表は営業出身で技術力は全然高くないですw)
そうした訳で、現在リンガルボックスではCTO(Co-founder)を募集中です。

EdTech、Adaptive Learning、Single Page Application(リンガルボックスではEmber.jsを利用しています。)、Node.js、Socket.io、WebRTC、Ruby on RailsREST API、アプリ開発といったキーワードにピンときた方は是非hiro(at)lingualbox.comまでご連絡下さい。