複数のプロバイダを持つサーバレスOAuth


この例では、複数のOAuthプロバイダ(この場合はGoogleとGitHub)を使用して、Begin.com or Architect . 認証はユーザが誰であるか、そして、承認(許可とも呼ばれます)はユーザが見ているか、するかです.両方とも重要です、しかし、認証だけはここでカバーされます.いいえauthライブラリ、サービスやプロバイダSDKのが使用されます.λを指定すると、依存関係が高速になります.Begin.com 特にこのベストプラクティスを奨励するために、依存関係を5 MBに制限します.
OAuthコードにフォーカスするには、このアプリは可能な限り最小限です.The app.arc 以下のマニフェストファイルは5つのルートを示します.最初のルート/ ゲストと認証ユーザーにアクセス可能です.The /admin ルートは認証されたユーザにのみ表示され、/login を返します.
#app.arc
@app
oauth-example

@http
get /
get /admin
get /auth
get /login
post /logout
全体のアプリを見てクローンを自由に感じるoauth-example repo . あなた自身のためにそれを試みるために、あなたは始めるために、直接それを展開することができます.

OAuth概要


基本的なoauthの流れを以下に示します.ユーザーがアプリケーションにログインを要求し、利用可能なプロバイダのいずれかに認証するオプションを提示されます.これらのプロバイダへのリンクは、プロバイダから直接ユーザーから要求を送信します.プロバイダに署名した後、応答をトークンでサーバーに送信します.サーバーは、そのトークンを使用して、ユーザープロファイルのプロバイダーに要求を送信するためにそのトークンを使用します.応答を使用して、ユーザーがアプリケーションに認証されます.

ログイン要求


ユーザーリクエスト/login (またはそこにリダイレクトされます)、ルートは各プロバイダのURLを生成します.それらがリダイレクトされるならば、「次の」パラメタ(すなわち)./login?next=admin ) 元のページに戻る.The next ユーザーが認証後に悪意のあるサイトに指示されるのを防ぐために、有効なオプション(ここで管理者だけ)に対して、パラメータはチェックされます.
//src/http/get-login/index.js
const arc = require('@architect/functions');
const githubOAuthUrl = require('./githubOAuthUrl');
const googleOAuthUrl = require('./googleOAuthUrl');

async function login(req) {
  let finalRedirect = '/';
  if (req.query.next === 'admin') {
    finalRedirect = '/admin';
  }
  const googleUrl = await googleOAuthUrl({ finalRedirect });
  const githubUrl = githubOAuthUrl({ finalRedirect });
  return {
    status: 200,
    html: `<!doctype html>
            <html lang="en">
                <head>
                    <meta charset="utf-8">
                    <meta name="viewport" content="width=device-width, initial-scale=1">
                    <title>login page</title>
                </head>
                <body>
                    <h1>Login</h1></br>
                    <a href="${githubUrl}">Login with Github</a></br>
                    <a href="${googleUrl}">Login with Google</a>
                </body>
            </html>`,
  };
}

exports.handler = arc.http.async(login);

状態パラメータ


The state クエリパラメータは、返されるリクエストがサーバーによって開始されたことを確認するのに役立ちます.つのオプションは、サーバーによって格納された安全な乱数を生成し、それから戻ってくる要求にマッチする.この例ではJSON Webトークン(JWT)を使用します.JWTは暗号化されたペイロードであり、プロバイダーと最後のリダイレクトの場所を含んでいるnext パラメータ).このJWTには1時間の満了設定があります.認証を完了するのに十分な長さのままである必要があるだけです.
//src/http/get-login/githubOAuthUrl.js
const jwt = require('jsonwebtoken');
module.exports = function githubOAuthUrl({ finalRedirect }) {
  let client_id = process.env.GITHUB_CLIENT_ID;
  let redirect_uri = encodeURIComponent(process.env.AUTH_REDIRECT);
  let state = jwt.sign(
    {
      provider: 'github',
      finalRedirect,
    },
    process.env.APP_SECRET,
    { expiresIn: '1 hour' }
  );
  let url = `https://github.com/login/oauth/authorize?client_id=${client_id}&redirect_uri=${redirect_uri}&state=${state}`;
  return url;
};

グーグルURL


Google URLは、Github機能に似ていますGET トップでのリクエスト.GoogleのOAuthドキュメントは、「OpenID設定」ドキュメントへの要求によって認証エンドポイントを検証することを推奨します.Googleが実際のエンドポイントパスを変更すると、このドキュメントで更新されます.このドキュメントは攻撃的にキャッシュされます、そして、要請は通常そこから返されます.
//src/http/get-login/googleOAuthUrl.js
const tiny = require('tiny-json-http');
const jwt = require('jsonwebtoken');

module.exports = async function googleOAuthUrl({ finalRedirect }) {
  const googleDiscoveryDoc = await tiny.get({
    url: 'https://accounts.google.com/.well-known/openid-configuration',
    headers: { Accept: 'application/json' },
  });
  const authorization_endpoint = googleDiscoveryDoc.body.authorization_endpoint;
  const state = await jwt.sign(
    {
      provider: 'google',
      finalRedirect,
    },
    process.env.APP_SECRET,
    { expiresIn: '1 hour' }
  );
  const options = {
    access_type: 'online',
    scope: ['profile', 'email'],
    redirect_uri: process.env.AUTH_REDIRECT,
    response_type: 'code',
    client_id: process.env.GOOGLE_CLIENT_ID,
  };
  const url = `${authorization_endpoint}?access_type=${options.access_type}&scope=${encodeURIComponent(
    options.scope.join(' ')
  )}&redirect_uri=${encodeURIComponent(options.redirect_uri)}&response_type=${options.response_type}&client_id=${
    options.client_id
  }&state=${state}`;

  return url;
};

プロバイダからのAuthリダイレクト


ユーザーが彼らの選ばれたプロバイダー(GoogleまたはGitHub)で署名したあと、彼らは/auth Aとルートcode and state パラメータセット.状態はプロバイダーに送られた全く同じ状態でなければなりません.コードパラメーターは、ユーザープロファイル情報にアクセスするためのトークンです.のためのルートハンドラ/auth 以下に示す.これは、要求がアプリケーションによって開始され、どのプロバイダが承認コードを送信したかを確認するために状態JWTをデコードします.
//src/http/get-auth/index.js
const arc = require('@architect/functions');
const githubAuth = require('./githubAuth');
const googleAuth = require('./googleAuth');
const jwt = require('jsonwebtoken');

async function auth(req) {
  let account = {};
  let state;
  if (req.query.code && req.query.state) {
    try {
      state = jwt.verify(req.query.state, process.env.APP_SECRET);
      if (state.provider === 'google') {
        account.google = await googleAuth(req);
        if (!account.google.email) {
          throw new Error();
        }
      } else if (state.provider === 'github') {
        account.github = await githubAuth(req);
        if (!account.github.login) {
          throw new Error();
        }
      } else {
        throw new Error();
      }
    } catch (err) {
      return {
        status: 401,
        body: 'not authorized',
      };
    }
    return {
      session: { account },
      status: 302,
      location: state.finalRedirect,
    };
  } else {
    return {
      status: 401,
      body: 'not authorized',
    };
  }
}

exports.handler = arc.http.async(auth);

リクエストユーザープロファイル


OAuthシーケンスの最終ステップは、ユーザープロファイルを取得することです.ギタブAPOST リクエストはcode とともにclient_id and client_secret . githubはアクセストークンで応答します.このトークンは、GET ユーザプロファイルの要求.このユーザープロファイルは、最終的に建築家/開始セッションに格納され返されます.ユーザーは今、アプリケーションへのさらなる要求のために認証されます.
//src/http/get-auth/githubAuth.js
const tiny = require('tiny-json-http');

module.exports = async function githubAuth(req) {
  try {
    let result = await tiny.post({
      url: 'https://github.com/login/oauth/access_token',
      headers: { Accept: 'application/json' },
      data: {
        code: req.query.code,
        client_id: process.env.GITHUB_CLIENT_ID,
        client_secret: process.env.GITHUB_CLIENT_SECRET,
        redirect_uri: process.env.AUTH_REDIRECT,
      },
    });
    let token = result.body.access_token;
    let user = await tiny.get({
      url: `https://api.github.com/user`,
      headers: {
        Authorization: `token ${token}`,
        Accept: 'application/json',
      },
    });
    return {
      name: user.body.name,
      login: user.body.login,
      id: user.body.id,
      url: user.body.url,
      avatar: user.body.avatar_url,
    };
  } catch (err) {
    return {
      error: err.message,
    };
  }
};
また、Googleは最終認証の前にトークンエンドポイントを検証する必要がある.再び、この応答は、不必要な要求を最小にするために積極的にキャッシュされます.Githubでユーザープロファイルを取得するにはPOST アクセストークンとGET ユーザープロファイルのトークンを使用します.Googleは、これらの2を結合します.とPOST リクエストを受信するid_token これはユーザプロファイルを持つJWTです.JWTは、それからユーザープロファイル情報を得るためにデコードされる.
//src/http/get-auth/googleAuth.js
const tiny = require('tiny-json-http');
const jwt = require('jsonwebtoken');

module.exports = async function googleAuth(req) {
  let googleDiscoveryDoc = await tiny.get({
    url: 'https://accounts.google.com/.well-known/openid-configuration',
    headers: { Accept: 'application/json' },
  });
  let token_endpoint = googleDiscoveryDoc.body.token_endpoint;

  let result = await tiny.post({
    url: token_endpoint,
    headers: { Accept: 'application/json' },
    data: {
      code: req.query.code,
      client_id: process.env.GOOGLE_CLIENT_ID,
      client_secret: process.env.GOOGLE_CLIENT_SECRET,
      redirect_uri: process.env.AUTH_REDIRECT,
      grant_type: 'authorization_code',
    },
  });
  return jwt.decode(result.body.id_token);
};

プロバイダとセットアップauAuth


GoogleとGitHubで認証するには、両方のプロバイダでこれを設定する必要があります.

ギタブセットアップ


For Github.com への移動:設定->開発者の設定-> OAuth Apps ->新しいOAuthアプリ.そこから必要に応じてフォームを記入します.コールバックURLがアプリケーションの完全なドメインとパスに一致することを確認します.ステージングと生産のためのドメインは、開始設定で見つけることができます.詳細はGithub Docs .

グーグルセットアップ


Googleコンソールは、より複雑に移動します.開発者アカウントのサインアップを開始するhttps://console.cloud.google.com/ . 新しいプロジェクトを設定し、“API&サービス”ダッシュボードに移動します.外部ユーザーの「oauth同意画面」を設定します.次に、資格情報を選択します.

満水流量


完全なAuAuthフロー図を以下に示します.Googleのみのステップは赤とgithubで示されているだけ青色で表示されます.