برنامه نویسی

گام به گام بدون سرور در AWS بیاموزید – احراز هویت

TL; DR

در این مجموعه سعی می‌کنم اصول بی‌سرور را در AWS توضیح دهم تا بتوانید برنامه‌های بدون سرور خود را بسازید. با آخرین مقالات، ما با هم یاد گرفتیم که چگونه توابع Lambda، APIهای استراحت، پایگاه‌های داده و ذخیره‌سازی فایل را ایجاد کنیم. در این مقاله، ما یاد خواهیم گرفت که چگونه با استفاده از Cognito، احراز هویت را به API های خود اضافه کنیم، ورود به سیستم را مدیریت کنیم و مسیرهای API محافظت شده ایجاد کنیم.

مقدمه ای بر Cognito

Cognito سرویس احراز هویت AWS است. این امکان را به شما می دهد تا مجموعه های کاربری ایجاد کنید که حاوی اطلاعات کاربران شما (نام کاربری، ایمیل، رمز عبور و غیره) است که به روشی امن و ایمن ذخیره می شوند. با وصل شدن به این مجموعه‌های کاربری، می‌توانید کلاینت‌های جمع کاربران ایجاد کنید، که برنامه‌هایی هستند که از مجموعه‌های کاربری برای احراز هویت کاربران استفاده می‌کنند. به عنوان مثال، می توانید یک مجموعه کاربری برای برنامه وب خود و دیگری برای برنامه تلفن همراه خود ایجاد کنید. هر یک از این برنامه‌ها App Client خود را دارند و می‌توانند با استفاده از همان دسته کاربری، کاربران را احراز هویت کنند.

به راحتی می توان با استفاده از این طرح توصیف کرد، جایی که نظرسنجی کاربر عنصر مرکزی است، و کاربران می توانند از طریق یک برنامه frontend یا یک تابع لامبدا متصل شوند.

پس از یادگیری طرح

هدف امروز – ایجاد یک مسیر API محافظت شده توسط یک مجوز

امروز، یک مسیر API ساده ایجاد می کنیم که توسط یک مجوز محافظت می شود. این مجوزدهنده یک مجموعه کاربران Cognito خواهد بود و به ما اجازه می‌دهد تا کاربران را قبل از دسترسی به مسیر API احراز هویت کنیم. ما همچنین یک ثبت نام، تأیید و یک مسیر ورود ایجاد خواهیم کرد که به کاربران امکان می دهد هویت خود را تأیید کنند و یک توکن JWT دریافت کنند که برای احراز هویت خود در مسیر محافظت شده استفاده می شود. از نظر معماری، به شکل زیر خواهد بود:

طرح قوس ها

برای ساخت این زیرساخت، از CDK AWS ترکیب شده با TypeScript استفاده خواهم کرد. من قبلاً از این روش استقرار در آخرین مقالات این مجموعه استفاده کردم، در صورت نیاز به تجدید نظر آنها را بررسی کنید.

یک User Pool و یک User Pool Client ایجاد کنید

با استفاده از AWS CDK، ایجاد یک مجموعه کاربری و یک کلاینت برنامه بصری است.

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class MyFirstStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const userPool = new cdk.aws_cognito.UserPool(this, 'myFirstUserPool', {
      selfSignUpEnabled: true,
      autoVerify: {
        email: true,
      },
    });

    const userPoolClient = new cdk.aws_cognito.UserPoolClient(this, 'myFirstUserPoolClient', {
      userPool,
      authFlows: {
        userPassword: true,
      },
    });
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

دو مرحله: من یک User Pool با گزینه‌هایی ایجاد می‌کنم که به کاربران امکان می‌دهد خودشان ثبت‌نام کنند و یک ایمیل تأیید در آدرس خود دریافت کنند. سپس، من این مجموعه کاربر را به یک سرویس گیرنده جمع کاربران متصل می کنم، که برای احراز هویت کاربران استفاده می شود. را فعال می کنم userPassword جریان احراز هویت، که به کاربران این امکان را می دهد تا با استفاده از نام کاربری و رمز عبور خود را احراز هویت کنند.

ثبت نام، تایید آدرس ایمیل و ورود به سیستم

در این آموزش سعی می کنم آن را ساده نگه دارم. من سه مسیر API احراز هویت ایجاد خواهم کرد: مسیر ثبت نام به کاربران امکان می دهد یک حساب کاربری ایجاد کنند و یک کد از طریق ایمیل دریافت کنند، مسیر تأیید به آنها اجازه می دهد حساب خود را تأیید کنند، و مسیر ورود به سیستم به کاربران اجازه می دهد تا خود را احراز هویت کنند و دریافت کنند. یک توکن JWT

بیایید با ارائه 3 تابع لامبدا مربوط به این 3 مسیر شروع کنیم.

// ... Previous code ...

// Provision a signup lambda function
const signup = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'signup', {
  entry: path.join(__dirname, 'signup', 'handler.ts'),
  handler: 'handler',
  environment: {
    USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
  },
});

// Give the lambda function the permission to sign up users
signup.addToRolePolicy(
  new cdk.aws_iam.PolicyStatement({
    actions: ['cognito-idp:SignUp'],
    resources: [userPool.userPoolArn],
  }),
);

// Provision a signup lambda function
const confirm = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'confirm', {
  entry: path.join(__dirname, 'confirm', 'handler.ts'),
  handler: 'handler',
  environment: {
    USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
  },
});

// Give the lambda function the permission to sign up users
confirm.addToRolePolicy(
  new cdk.aws_iam.PolicyStatement({
    actions: ['cognito-idp:ConfirmSignUp'],
    resources: [userPool.userPoolArn],
  }),
);

// Provision a signin lambda function
const signin = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'signin', {
  entry: path.join(__dirname, 'signin', 'handler.ts'),
  handler: 'handler',
  environment: {
    USER_POOL_CLIENT_ID: userPoolClient.userPoolClientId,
  },
});

// GIve the lambda function the permission to sign in users
signin.addToRolePolicy(
  new cdk.aws_iam.PolicyStatement({
    actions: ['cognito-idp:InitiateAuth'],
    resources: [userPool.userPoolArn],
  }),
);
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این قطعه کد، من سه تابع لامبدا ایجاد می کنم، یکی برای مسیر ثبت نام، یکی برای مسیر تایید و دیگری برای مسیر ورود به سیستم. شناسه مشتری جمع‌آوری کاربر را نیز به آنها ارسال می‌کنم که برای احراز هویت کاربران استفاده می‌شود.

در نهایت، مجوزهای IAM مورد نیاز را به توابع لامبدا اضافه می‌کنم تا به آنها اجازه تعامل با مجموعه کاربر را بدهم. اولین تابع لامبدا به این نیاز دارد cognito-idp:SignUp اجازه، دومی نیاز دارد cognito-idp:ConfirmSignUp و سومی نیاز خواهد داشت cognito-idp:InitiateAuth اجازه.

مرحله بعد، کد داخل این توابع لامبدا را ایجاد کنید. بیایید با تابع ثبت نام شروع کنیم.

import { CognitoIdentityProviderClient, SignUpCommand } from '@aws-sdk/client-cognito-identity-provider';

const client = new CognitoIdentityProviderClient({});

export const handler = async (event: { body: string }): Promise<{ statusCode: number; body: string }> => {
  const { username, password, email } = JSON.parse(event.body) as {
    username?: string;
    password?: string;
    email?: string;
  };

  if (username === undefined || password === undefined || email === undefined) {
    return Promise.resolve({ statusCode: 400, body: 'Missing username, email or password' });
  }

  const userPoolClientId = process.env.USER_POOL_CLIENT_ID;

  await client.send(
    new SignUpCommand({
      ClientId: userPoolClientId,
      Username: username,
      Password: password,
      UserAttributes: [
        {
          Name: 'email',
          Value: email,
        },
      ],
    }),
  );

  return { statusCode: 200, body: 'User created' };
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در این قطعه کد، با استفاده از SDK، یک دستور SignUp را به جمع کاربران ارسال می کنم. ClientId را از متغیر محیطی که قبلاً در قسمت تدارکات کد مشخص کردم دریافت می کنم.

تابع تایید بسیار شبیه است.

import { CognitoIdentityProviderClient, ConfirmSignUpCommand } from '@aws-sdk/client-cognito-identity-provider';

const client = new CognitoIdentityProviderClient({});

export const handler = async (event: { body: string }): Promise<{ statusCode: number; body: string }> => {
  const { username, code } = JSON.parse(event.body) as { username?: string; code?: string };

  if (username === undefined || code === undefined) {
    return Promise.resolve({ statusCode: 400, body: 'Missing username or confirmation code' });
  }

  const userPoolClientId = process.env.USER_POOL_CLIENT_ID;

  await client.send(
    new ConfirmSignUpCommand({
      ClientId: userPoolClientId,
      Username: username,
      ConfirmationCode: code,
    }),
  );

  return { statusCode: 200, body: 'User confirmed' };
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

کنترل کننده انتظار یک کد تأیید را دارد که از طریق ایمیل توسط کاربری که می خواهد ثبت نام کند دریافت کرده است. سپس، یک فرمان ConfirmSignUp را به جمع کاربران راه اندازی می کند.

در نهایت، تابع sign in.

import { CognitoIdentityProviderClient, InitiateAuthCommand } from '@aws-sdk/client-cognito-identity-provider';

const client = new CognitoIdentityProviderClient({});

export const handler = async (event: { body: string }): Promise<{ statusCode: number; body: string }> => {
  const { username, password } = JSON.parse(event.body) as { username?: string; password?: string };

  if (username === undefined || password === undefined) {
    return Promise.resolve({ statusCode: 400, body: 'Missing username or password' });
  }

  const userPoolClientId = process.env.USER_POOL_CLIENT_ID;

  const result = await client.send(
    new InitiateAuthCommand({
      AuthFlow: 'USER_PASSWORD_AUTH',
      ClientId: userPoolClientId,
      AuthParameters: {
        USERNAME: username,
        PASSWORD: password,
      },
    }),
  );

  const idToken = result.AuthenticationResult?.IdToken;

  if (idToken === undefined) {
    return Promise.resolve({ statusCode: 401, body: 'Authentication failed' });
  }

  return { statusCode: 200, body: idToken };
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این تابع یک نام کاربری و یک رمز عبور را انتظار دارد. سپس، دستور InitiateAuth را به جمع کاربر راه اندازی می کند. این دستور یک توکن id که یک توکن JWT است برمی گرداند. این نشانه می تواند بعداً توسط کاربر برای احراز هویت استفاده شود.

API و مسیر محافظت شده را ایجاد کنید

اکنون که توابع لامبدا ایجاد شده اند، می توانم API و مسیر محافظت شده را ایجاد کنم. من از ساختار API Gateway از CDK AWS استفاده خواهم کرد.

// ... previous code ...

// Create a new API
const myFirstApi = new cdk.aws_apigateway.RestApi(this, 'myFirstApi', {});

// Add routes to the API
myFirstApi.root.addResource('sign-up').addMethod('POST', new cdk.aws_apigateway.LambdaIntegration(signup));
myFirstApi.root.addResource('sign-in').addMethod('POST', new cdk.aws_apigateway.LambdaIntegration(signin));
myFirstApi.root.addResource('confirm').addMethod('POST', new cdk.aws_apigateway.LambdaIntegration(confirm));
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این قطعه کد یک API جدید ایجاد می کند و سه مسیر را به آن اضافه می کند. هر مسیر به یک تابع لامبدا مرتبط است. ساختار API Gateway به طور خودکار مجوزهای لازم را ایجاد می کند تا به API اجازه دهد توابع لامبدا را فعال کند.

حال، بیایید یک مجوز بر اساس جمع کاربران Cognito ایجاد کنیم و آن را به مسیر جدیدی که می‌خواهیم از آن محافظت کنیم، اختصاص دهیم.

// ... previous code ...

// Create an authorizer based on the user pool
const authorizer = new cdk.aws_apigateway.CognitoUserPoolsAuthorizer(this, 'myFirstAuthorizer', {
  cognitoUserPools: [userPool],
  identitySource: 'method.request.header.Authorization',
});

const secretLambda = new cdk.aws_lambda_nodejs.NodejsFunction(this, 'secret', {
  entry: path.join(__dirname, 'secret', 'handler.ts'),
  handler: 'handler',
});

// Create a new secret route, triggering the secret Lambda, and protected by the authorizer
myFirstApi.root.addResource('secret').addMethod('GET', new cdk.aws_apigateway.LambdaIntegration(secret), {
  authorizer,
  authorizationType: cdk.aws_apigateway.AuthorizationType.COGNITO,
});
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

در نهایت، کد (ساده) برای تابع مخفی لامبدا.

export const handler = async (): Promise<{ statusCode: number; body: string }> => {
  return Promise.resolve({ statusCode: 200, body: 'CAUTION !!! THIS IS VERY SECRET' });
};
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

استقرار و آزمایش برنامه

ما تمام شدیم! مرحله آخر، برنامه را نصب کرده و آن را آزمایش کنید. برای استقرار برنامه، دستور زیر را اجرا کنید.

npm run cdk deploy
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

می‌توانید URL API را در کنسول AWS، در بخش API Gateway پیدا کنید. در مقاله اول این مجموعه به تفصیل توضیح دادم، با خیال راحت آن را بررسی کنید!

زمان آزمایش همه چیز است! ابتدا بیایید ثبت نام کنیم، نام کاربری، ایمیل و رمز عبور را مشخص می کنم.

ثبت نام

پس از ثبت نام، یک ایمیل با یک کد تایید دریافت می کنم.

کد تایید

سپس با وارد کردن کدی که از طریق ایمیل دریافت کرده ام و نام کاربری خود ثبت نام را تایید می کنم.

ثبت نام را تایید کنید

در نهایت می توانم با نام کاربری و رمز عبور وارد شوم.

ورود

نتیجه این درخواست یک توکن JWT است. من می توانم از این نشانه برای دسترسی به مسیر مخفی استفاده کنم، با عبور آن در هدر Authorization با این قالب: “Bearer”

مسیر مخفی

همه چیز همانطور که انتظار می رود کار می کند!

نتیجه

این آموزش مقدمه ای بسیار کم عمق برای احراز هویت بدون سرور در Cognito است. این یک شروع ساده برای ثبت نام و احراز هویت کاربران است. بسیاری از ویژگی‌های دیگر نیز وجود دارد که می‌توان از آنها استفاده کرد، مانند احراز هویت چند عاملی، احراز هویت UI میزبانی شده در دامنه خود و موارد دیگر. شاید در مقاله آینده به این موضوعات بپردازم!

قصد دارم این سری مقالات را به صورت دو ماه یکبار ادامه دهم. من قبلاً ایجاد توابع لامبدا ساده و APIهای REST و همچنین تعامل با پایگاه‌های داده DynamoDB و سطل‌های S3 را پوشش داده‌ام. شما می توانید این پیشرفت را در مخزن من دنبال کنید! من موضوعات جدیدی مانند ایجاد برنامه های کاربردی رویداد محور، ایمنی نوع و موارد دیگر را پوشش خواهم داد. اگر پیشنهادی دارید، دریغ نکنید با من تماس بگیرید!

اگر واکنش نشان دهید و این مقاله را با دوستان و همکاران خود به اشتراک بگذارید، واقعاً ممنون می شوم. این به من کمک زیادی می کند تا مخاطبانم را افزایش دهم. همچنین فراموش نکنید که مشترک شوید تا با انتشار مقاله بعدی به روز شوید!

من می خواهم در تماس بمانید اینجا من است حساب توییتر. من اغلب مطالب جالبی درباره AWS و بدون سرور پست می‌کنم یا دوباره پست می‌کنم، لطفاً مرا دنبال کنید!

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا