/var/log/messages

Apr 2, 2018 - 8 minute read - Comments - angular

Admin Authorization

auth0 な認証まわりで良い資料がない、と思ってたら本家にあったのでぐーぐる訳使って動作の確認してみましたので翻訳の一部を以下に。

なんでこんな作りにしているのかは謎。以下なコンテンツの Admin Authorization の部分のみ、です。

Admin Authorization

私たちのRSVPアプリでは、管理者権限を持つユーザーだけがイベントの作成、編集、削除ができるはずです。 他のすべてのユーザーは、これらのイベントにのみRSVPすることができます。 これを実装するには、Node.js APIとAngularアプリの両方でユーザーロールを割り当ててから利用する必要があります。 まず、関連するステップを見てみましょう。

  • Auth0ルールを使用してユーザーロールを確立し、ID(クライアント)トークンとアクセス(API)トークンに追加します。
  • Node.js APIにミドルウェアを実装して、管理者ユーザーだけが特定のAPIルートにアクセスできるようにします。
  • 特定のルートと機能へのアクセスを制限するには、角度アプリの役割情報を使用します。

始めましょう!

Use an Auth0 Rule for Admin Authorization

ルールは、ユーザーが認証するたびにAuth0が実行するJavaScript関数です。 これらは、認証機能を拡張する簡単な方法を提供します。 まず、Auth0ダッシュボードにログインし、新しいルールを作成します。 [ユーザーに役割を設定する]ルールテンプレートを選択します。

これにより、JavaScriptテンプレートが開きます。 現時点では、自分のアカウントに管理者の役割を割り当てたいだけです。 このルールの名前を変更して、そのルールの内容を一目で確認できるようにすることをお勧めします。 特定の電子メールドメインのindexOf()に対するユーザーの電子メールをチェックするテンプレートを簡単に変更できます。 これをGoogleのフルメールアドレスに変更します。これは、管理者の役割を割り当てるIDを表すためです。 ルールを次のように変更します。

// Set me as 'admin' role, and all others to 'user'
// Save app_metadata to ID and access tokens
function (user, context, callback) {
  user.app_metadata = user.app_metadata || {};
  var addRolesToUser = function(user, cb) {
    if (user.email && user.email === '[MY_FULL_GOOGLE_ACCOUNT_EMAIL]') {
      cb(null, ['admin']);
    } else {
      cb(null, ['user']);
    }
  };

  addRolesToUser(user, function(err, roles) {
    if (err) {
      callback(err);
    } else {
      user.app_metadata.roles = roles;
      auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
        .then(function(){
          // Add metadata to both ID token and access token
          var namespace = 'http://myapp.com/roles';
          var userRoles = user.app_metadata.roles;
          context.idToken[namespace] = userRoles;
          context.accessToken[namespace] = userRoles;
          callback(null, user, context);
        })
        .catch(function(err){
          callback(err);
        });
    }
  });
}

[MY_FULL_GOOGLE_ACCOUNT_EMAIL]を自分の資格情報で置き換えます。 例のルールテンプレートのように、単にドメインではなく完全な電子メールアドレスに一致させるため、indexOf()を厳密な等式で置き換えています。

注:Google以外のアカウントを使用する場合は、適切なプロパティでアカウントを識別してください。 すべての接続タイプによってすべてのプロパティが返されるわけではありません。 また、その電子メールのすべてのアカウントを管理者として設定する場合や、Googleアカウントとユーザー名/パスワードの両方のアカウントのみをチェックに一致させたい場合は、アカウントの詳細をより明示的に指定することもできます。 Auth0ユーザーを確認するか、Auth0ソーシャルコネクションをテストして、異なるアイデンティティプロバイダからのログインから返され格納されるデータの種類を確認することができます。

ロール配列を持つapp_metadataをユーザーに追加しましたが、これはOpenID標準のクレームの一部ではないため、updateAppMetadata()約束が解決されたときにIDとアクセストークンにロールデータを含めるためにカスタムクレームを追加する必要があります 。 名前空間識別子は、Auth0以外のHTTPまたはHTTPS URLでもかまいません。実際のリソースを指す必要はありません。 Auth0は、追加の申し立てについてOIDCからのこの勧告を強制し、名前空間を持たないクレームを黙って除外します。 ここでAuth0を使用してカスタムクレームを実装する方法について詳しく読むことができます。 私たちのカスタムクレームの鍵はhttp://myapp.com/rolesです。 これは、AngularアプリとNode APIのIDとアクセストークンからロール配列を取得する方法です。 私たちのルールは、Auth0ユーザーのapp_metadata.rolesをこのプロパティに割り当てます。

終了したら、このルールを保存するために “保存”ボタンをクリックしてください。

Sign In with Admin Account

次に行う必要があるのは、意図した管理者のユーザーでログインすることです。 これによりルールが実行され、アプリのメタデータがターゲットアカウントに追加されます。 その後、ユーザーがログインするたびに、ロールデータもトークンで利用できるようになります。 すでにAngularアプリでログインを実装しているので、私たちが行う必要があるのは、Set rolesで指定したアカウントでユーザールールにサインインするだけです。 ブラウザーのAngularアプリにhttp:// localhost:4200でアクセスし、ヘッダーに追加した「ログイン」リンクをクリックします。

注:Setロールをユーザールールに設定するときはGoogleアカウントを使用したので、Google OAuthを使用してログインします。 (Auth0アカウントとセットアップの第1部のAuth0 DashboardソーシャルコネクションのGoogle OAuthを有効にしました。)

ログインしたら、適切なロールがAuth0 Dashboard Usersセクションのユーザーアカウントに追加されたことを確認することができます。 ログインしたユーザーを探し、名前をクリックして詳細を表示します。 このユーザーのメタデータセクションは次のようになります。

Admin Middleware in Node API

認証で役割サポートができたので、これを使用して管理者のアクセスが必要なAPIルートを保護できます。 サーバーのconfig.jsファイルを開き、Add user role to tokens ruleを作成するときに使用した名前空間を持つNAMESPACEプロパティーを追加します。

// server/config.js
module.exports = {
  ...,
  NAMESPACE: 'http://myapp.com/roles'
};

注:Auth0 Rulesダッシュボードで既存のルールテンプレートを変更し、上記のサンプルコードと一致するように名前空間を変更しなかった場合は、設定を設定する際に注意が必要です。

ユーザーが認証され、APIエンドポイントにアクセスするための管理者権限を持つことを確認するミドルウェアを実装できるようになりました。

server api.jsファイルに次のように追加します。

// server/api.js
...
module.exports = function(app, config) {
  // Authentication middleware
  const jwtCheck = jwt({
    ...
  });

  // Check for an authenticated admin user
  const adminCheck = (req, res, next) => {
    const roles = req.user[config.NAMESPACE] || [];
    if (roles.indexOf('admin') > -1) {
      next();
    } else {
      res.status(401).send({message: 'Not authorized for admin access'});
    }
  }

...

私たちのユーザーロールをトークンに追加ルールは、IDとアクセストークンに以下のキー/値のペアを追加しました:

http://myapp.com/roles": ["admin"]

express-jwtパッケージは、デコードされたトークンをデフォルトでreq.userに追加します。 adminCheckミドルウェアはこのプロパティーを見つけ、配列内のadminの値を探します。 見つかった場合、要求は続行されます。 そうでない場合は、401 Unauthorizedステータスが返され、短いエラーメッセージが返されます。 現在、APIは管理者の役割を処理するように設定されています。

Admin Authorization in Angular App

フロントエンドは、ユーザーが管理者であるかどうかを知りたいので、AuthServiceを更新してこの情報を取得して保存してみましょう。 まず、AUTH_CONFIGに名前空間を格納する必要があります。 auth.config.tsファイルを開き、NAMESPACEキーを追加します。

// src/app/auth/auth.config.ts
...
interface AuthConfig {
  ...,
  NAMESPACE: string;
};

export const AUTH_CONFIG: AuthConfig = {
  ...,
  NAMESPACE: 'http://myapp.com/roles'
};

名前空間が保存されたので、管理ステータスをauth.service.tsファイルに保存するサポートを追加しましょう:

// src/app/auth/auth.service.ts
...
export class AuthService {
  ...
  isAdmin: boolean;
  ...
  constructor(private router: Router) {
    // If authenticated, set local profile property,
    // admin status, and update login status subject.
    // If token is expired but user data still in localStorage, log out
    if (this.tokenValid) {
      this.userProfile = JSON.parse(localStorage.getItem('profile'));
      this.isAdmin = localStorage.getItem('isAdmin') === 'true';
      this.setLoggedIn(true);
    }
  }

  ...

  private _setSession(authResult, profile) {
    // Save session data and update login status subject
    ...
    this.isAdmin = this._checkAdmin(profile);
    localStorage.setItem('isAdmin', this.isAdmin.toString());
    this.setLoggedIn(true);
  }

  private _checkAdmin(profile) {
    // Check if the user has admin role
    const roles = profile[AUTH_CONFIG.NAMESPACE] || [];
    return roles.indexOf('admin') > -1;
  }

  logout() {
    // Ensure all auth items removed from localStorage
    ...
    localStorage.removeItem('isAdmin');
    // Reset local properties, update loggedIn$ stream
    this.userProfile = undefined;
    this.isAdmin = undefined;
    this.setLoggedIn(false);
  }

  ...

最初にisAdmin:booleanという新しいプロパティを追加します。 これにより、ユーザーの管理ステータスが保存され、フロントエンドで使用できるようになります。 コンストラクタでは、ユーザが認証されると、ローカルストレージ内のisAdminキーが検索されます。 ローカルストレージは値を文字列として格納するので、ブール値としてキャストします。 次に、_setSession()関数を更新します。 ローカルのuserProfileプロパティを設定した後は、private _checkAdmin()メソッドを使用して、そのユーザーが自分の役割にadminを持っているかどうかを判断します。

注意:型はbooleanですが、ローカルストレージは文字列を要求するため、isAdminを文字列にキャストする必要があります。

最後に、ローカルストレージからisAdminデータを削除し、logout()メソッドでサービスを削除します。 ユーザーがクライアント側で管理者権限を持っているかどうかを確認できるようになりました。

セキュリティ上の注意:これは決してクライアント側では行わないでください。 上記のAPIミドルウェアのセクションで行ったように、APIルートも常に保護されていることを確認してください。

APIとAngularアプリの両方で管理者権限が設定されました。 私たちはアプリケーションを開発する際に、これをさらに多くします!

Git Tutorial ナイスユカリ

comments powered by Disqus