私は、emberjsでフロントエンドを使用し、nodejsサーバーでバックエンド/サーバー側を使用してアプリケーションを作成しています。ユーザーがサードパーティOauth(google、Twitter、Facebook)でログイン/サインアップできるようにemberjsを構成しました。RESTfulAPIをホストするエクスプレスnodejsサーバーで記述されたバックエンドがあります。
私はDBをemberjsに接続しておらず、厳密にはクライアント側のコードなので、とにかくすべきではないと思います。クライアント側とサーバー側の間の通信にJWTを使用することを計画しています。ユーザーがoauth credでログインすると、プロバイダーからuid、name、login、access_tokenおよびその他の詳細を含むJSONオブジェクトが返されます。
ユーザーサインアップの処理方法に関する戦略の選択に苦労しています。 OAuthであるため、サインアッププロセスはありません。したがって、フローは、ユーザーがデータベースにない場合は作成します。メール/パスワード認証をサポートしていません。ユーザーがOAuthプロバイダーで初めてサインインするときのフローはどうなりますか?emberjsは、サインインごとにすべての詳細をバックエンドに送信して、バックエンドが新しいユーザーを追加できるようにしますdb?
JWT本体の一部は何ですか?私はuidとプロバイダーがアクセストークンを提供することを考えていました。ここで考えられる問題の1つは、プロバイダー固有のアクセストークンが変更される可能性があることです。ユーザーは、プロバイダーのサイトからトークンを取り消して、emberjsで再度サインアップできます。
他のjavascriptクライアントサイドフレームワークでフロントエンドを作成するのは、それが簡単になれば、私はオープンです。
動作だけでなく、セキュアなステートレス認証についても話している場合は、access
とrefresh
の両方のトークンを使用した適切な戦略を検討する必要があります。
アクセストークンは、保護されたリソースへのアクセスを提供するトークンです。 Expiration
ここでは、約1時間でインストールされる場合があります(考慮事項によって異なります)。
リフレッシュトークンは、期限切れになった場合やユーザーセッションが更新された場合に追加のaccess token
を生成するために使用される特別なトークンです。明らかに、(access token
と比較して)長寿命にし、可能な限り安全にする必要があります。 Expiration
ここでは、約10日またはそれ以上でインストールされる場合があります(考慮事項にも依存します)。
FYI:refresh tokens
は長寿命であるため、本当に安全にするために、データベースに保存することをお勧めします(トークンリクエストの更新が実行されます)めったにありません)。このように、リフレッシュトークンが何らかの方法でハッキングされ、誰かがaccess/refresh
トークンを再生成した場合でも、もちろん権限を失いますが、ログイン/パスを知っているので、システムにログインできます(後で使用する場合)またはソーシャルネットワーク経由でサインインするだけです。
これらのトークンを保存する場所は?
基本的に2つの一般的な場所があります。
いいのですが、同時に十分に危険です。ストレージは、同じドメインのJavaScriptコードを介してアクセスできます。つまり、 [〜#〜] xss [〜#〜] を取得した場合、トークンがハッキングされる可能性があります。したがって、この方法を選択する場合は、信頼できないすべてのデータを注意してエンコード/エスケープする必要があります。そして、たとえそれを行ったとしても、サードパーティのクライアント側モジュールを使用することはかなり確実であり、それらのいずれかに悪意のあるコードがあるという保証はありません。
また、Web Storage
は、転送中に安全な標準を強制しません。したがって、JWTがHTTPS
を介して送信され、HTTP
を送信しないようにする必要があります。
特定のHttpOnly
オプションを使用すると、CookieはJavaScriptを介してアクセスできず、XSSの影響を受けません。 Secure
Cookieフラグを設定して、CookieがHTTPS経由でのみ送信されるようにすることもできます。ただし、Cookieは異なるタイプの攻撃に対して脆弱です。クロスサイトリクエストフォージェリ( [〜#〜] csrf [〜#〜] )。この場合、CSRF
は、ある種の同期トークンパターンを使用することで防止できます。 AngularJS
の Security Considerations セクションに適切な実装があります。
記事 に従ってください。
一般的な仕組みを説明するには:
[〜#〜] jwt [〜#〜]自体に関するいくつかの言葉:
明確にするために、Auth0のユーザーからの本当にクールな JWTデバッガー があります。 public
、private
(および reserved )という2つの(場合によっては3つの)一般的なクレームタイプがあります。
JWT
bodyの例(ペイロード、好きなものを指定できます):
{
name: "Dave Doe",
isAdmin: true,
providerToken: '...' // should be verified then separately
}
JWT
構造の詳細については、 here を参照してください。
あなたが提示した2つの特定の質問に答えるには:
ユーザーがOAuthプロバイダーで初めてサインインするときのフローはどうなりますか?emberjsは、サインインごとにすべての詳細をバックエンドに送信して、バックエンドが新しいユーザーを追加できるようにしますdb?
ユーザーがサインアップするか、oauth経由でログインし、クライアントが新しいアクセストークンを受信するたびに、ユーザーテーブルにpsert(更新または挿入)します) (またはコレクション)とともに、oauthプロバイダーAPIからユーザーについて取得した新しい情報または更新された情報。アクセストークンと関連するプロファイル情報を確保するために、各ユーザーレコードに直接保存することをお勧めします通常、私は通常、これを新しいトークンが存在するときにこれらの手順を自動的に実行する何らかのミドルウェアに構成します。
JWT本体の一部は何ですか? uidとプロバイダーがアクセストークンを提供することを考えていました。ここで考えられる問題の1つは、プロバイダー固有のアクセストークンが変更される可能性があることです。ユーザーは、プロバイダーのサイトからトークンを取り消して、emberjsで再度サインアップできます。
JWT本体は通常、sers Claimsで構成されます。私は個人的には、プロバイダーのアクセストークンをJWTトークンの本体に格納しても、クライアントアプリにとってはほとんどメリットがないため、ほとんど利点がないと考えています(クライアントからAPIに直接API呼び出しをたくさん行う場合を除き、これらはサーバー側を呼び出し、アプリクライアントに、独自のインターフェイスに準拠する正規化されたクレームセットを送り返します)。独自のクレームインターフェイスを記述することにより、クライアントアプリの複数のプロバイダーから生じるさまざまな違いを回避する必要がなくなります。この例としては、APIでユーザープロファイルテーブルに保存する共通フィールドとは異なる名前のTwitterおよびFacebook固有のフィールドを結合し、ローカルプロファイルフィールドをJWT本文のクレームとして埋め込み、クライアントアプリによって解釈されるようにします。これには、暗号化されていないJWTトークンで将来漏洩する可能性のあるデータを永続化しないという追加の利点があります。
oauthプロバイダーが提供するアクセストークンをJWTトークン本体内に保存するかどうかに関係なく、プロファイルデータが変更されるたびに新しいJWTトークンを付与する必要があります。プロファイルの更新が発生せず、以前のトークンがまだ有効な場合、新しいJWTトークンの発行をバイパスします)。
JWTトークン本文にクレームとして保存するプロファイルフィールドに加えて、標準の JWTトークン本文フィールド を常に定義します。
{
iss: "https://YOUR_NAMESPACE",
sub: "{connection}|{user_id}",
aud: "YOUR_CLIENT_ID",
exp: 1372674336,
iat: 1372638336
}
すべてのOAuth=ワークフローでは、間違いなく passportjs ライブラリを使用する必要があります。完全なドキュメントも読む必要があります。理解しやすいですが、 OAuth 300以上のプロバイダーと発行トークンによる認証が含まれています。
それでも、手動で行う場合や基本的な理解が必要な場合は、ここで使用するフローを示します。
フロントエンドには、OAuthが実装されているGoogle/Facebookなどでのサインインをリストするログインページがあります。
成功OAuthはuid、login、access_tokenなどになります(JSONオブジェクト)
POST Node.jsアプリケーションの_/login/
_ルートへのJSONオブジェクト。(はい、新規ユーザーか既存ユーザーかに関係なく、応答全体を送信します。ここで追加データを送信します。 2つのリクエストを行うよりも優れています)
バックエンドアプリケーションは、uid
および_access_token
_を読み取ります。 _access_token
_が有効であることを確認するには、( https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken )または質問してくださいアクセストークンを使用するプロバイダーからのユーザーデータ用。 (OAuthアクセストークンはアプリ/開発者ごとに生成されるため、無効なアクセストークンでは失敗します))次に、バックエンドDBを検索します。
uid
がデータベースに存在する場合、DBでユーザーのaccess_tokenとexpiresInを更新します。 (access_tokenを使用すると、特定のユーザーについてFacebookからより多くの情報を取得でき、通常は数時間アクセスできます。)
それ以外の場合は、uid、loginなどの情報を持つ新しいユーザーを作成します。
Access_tokenを更新するか、新しいユーザーを作成したら、uid
を含むJWTトークンを送信します。 (jwtをシークレットでエンコードします。これにより、送信され、改ざんされていないことが保証されます。チェックアウト https://github.com/auth0/express-jwt )
ユーザーが_/login
_からjwtを受け取った後のフロントエンドで、sessionStorage.setItem('jwt', token);
によってsessionStorage
に保存します
フロントエンドで、次も追加します。
if ($window.sessionStorage.token) { xhr.setRequestHeader("Authorization", $window.sessionStorage.token); }
これにより、jwtトークンがある場合、すべてのリクエストで送信されます。
app.use(jwt({ secret: 'shhhhhhared-secret'}).unless({path: ['/login']}));
これにより、パス内のすべてのjwtが検証され、ユーザーがログインしていることが確認されます。そうでない場合は、ログインページへのアクセスとリダイレクトが許可されません。ここでの例外ケースは_/login
_です。これは、新規ユーザーまたは非認証ユーザーの両方にJWTを提供する場所だからです。
Github URLでトークンを取得する方法と、現在サービスを提供しているユーザーのリクエストを確認する方法に関する詳細情報を見つけることができます。