برنامه نویسی

کنترل جریان احراز هویت کاربر با Lambda & Cognito

سلب مسئولیت: تصویر قهرمان این پست نتیجه اعلان زیر بود AWS lambda and AWS cognito logos into a Renaissance paint. Use full logos and a less known painting. من فکر می کنم هنوز چیزهای زیادی برای یادگیری در مورد درخواست های تصویر هوش مصنوعی دارم 😅😅


احراز هویت یک موضوع مشترک بین بسیاری از انواع سیستم ها است. راه های مختلفی برای رسیدگی به آن وجود دارد و روش های ترجیحی من از خدمات مدیریت شده استفاده می کنند. من AWS Cognito را یک راه حل واقعاً عالی برای رسیدگی به احراز هویت به طور ویژه یافتم اگر بعداً برنامه تأیید شده را با سایر خدمات میزبانی شده وصل می کنید. Cognito راه‌هایی را برای مدیریت و اعتبارسنجی متقابل کاربران در برابر سرویس‌ها در اختیار شما قرار می‌دهد و اخیراً من از قلاب‌های آن برای ایجاد ویژگی‌های احراز هویت پیچیده‌تر استفاده کرده‌ام.

دانستن محرک ها

استخرهای کاربر Cognito دارای یک ویژگی به نام «محرک‌های Lambda» هستند که به شما امکان می‌دهد از Lambdas ایجاد شده قبلی برای انجام اقدامات سفارشی در طول چهار نوع جریان استفاده کنید:

  1. ثبت نام
  2. احراز هویت
  3. احراز هویت سفارشی (مانند CAPTCHA یا سوالات امنیتی)
  4. پیام رسانی

هر یک از این جریان ها دارای محرک های مختلفی هستند که کد لامبدا را در بین مراحل خاص جریان اجرا می کنند. ثبت نام به عنوان مثال دارد Pre sign-up trigger، Post confirmation trigger و Migrate user trigger که می تواند به یک تابع لامبدا متصل شود.

برای آزمایش ظرفیت تریگرهای لامبدا، سیستمی را توسعه خواهیم داد که از ورود به سیستم پس از 5 شکست متوالی جلوگیری می کند.

کدگذاری لامبدا

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

اولین ماشه لامبدا به این صورت خواهد بود

module.exports.preAuthTrigger = async (event) => {
    if (!(await this.isUserEnabled(event))) throw new Error('Usuário Bloqueado')

    const attempts = await this.getLoginAttempts(event)
    if (attempts > 4) {
        await this.disableUser(event)
        throw new Error('Usuário Bloqueado')
    }

    await this.updateLoginAttempts(event, attempts)
    return event
}
وارد حالت تمام صفحه شوید

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

اولین قدم ما این است که بررسی کنیم آیا کاربر قبلاً با تعداد تلاش ها مسدود شده است یا خیر. می توانیم با fn جداگانه این کار را انجام دهیم:

exports.isUserEnabled = async (event) => {
    const getParams = {
        UserPoolId: event.userPoolId,
        Username: event.userName,
    }
    const userData = await cognitoService.adminGetUser(getParams).promise()
    return userData.Enabled
}
وارد حالت تمام صفحه شوید

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

با این کار به ویژگی های کاربر در استخر کاربر cognito دسترسی پیدا می کنیم و آن را بررسی می کنیم Enable ویژگی که تعیین می کند که آیا کاربر قادر به استفاده از آن است username و password برای ورود به سیستم یک کاربر غیرفعال نمی تواند وارد یک استخر شناختی شود و این دقیقاً همان چیزی است که ما اینجا می خواهیم.

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

exports.getLoginAttempts = async (event) => {
    const getParams = {
        UserPoolId: event.userPoolId,
        Username: event.userName,
    }
    const userData = await cognitoService.adminGetUser(getParams).promise()
    const attribute = userData.UserAttributes.find(
        (att) => att.Name === 'custom:attempts'
    )
    if (attribute !== undefined && attribute !== null)
        return parseInt(attribute.Value)
    else return 0
}
وارد حالت تمام صفحه شوید

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

این یک فرآیند بسیار شبیه به fn قبلی است، اما اکنون ما به دنبال یک ویژگی سفارشی با نام هستیم custom:attempts که در مراحل بعدی در جمع کاربران خود ایجاد خواهیم کرد. اگر کاربر بیش از 5 بار تلاش داشته باشد (شمارش را از 0 شروع می کنیم)، باید کاربر را مسدود کنیم. یک تکه کیک:

exports.disableUser = async (event) => {
    await cognitoService
        .adminDisableUser({
            UserPoolId: event.userPoolId,
            Username: event.userName,
        })
        .promise()
}
وارد حالت تمام صفحه شوید

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

ما همچنین باید یک خطا ایجاد کنیم و اجرای لامبدا را متوقف کنیم زیرا این باعث می شود روند ورود به سیستم همانطور که می خواهیم شکست بخورد. اکنون که می‌توانیم کاربر را مسدود کنیم، فقط باید تعداد تلاش‌ها را در صورت مسدود نشدن به‌روزرسانی کنیم:

exports.updateLoginAttempts = async (event, attempts) => {
    const updateParams = {
        UserAttributes: [
            {
                Name: 'custom:login_attempts',
                Value: (attempts + 1).toString(),
            },
        ],
        UserPoolId: event.userPoolId,
        Username: event.userName,
    }

    await cognitoService.adminUpdateUserAttributes(updateParams).promise()
}
وارد حالت تمام صفحه شوید

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

این آخرین تابع همه چیز را برای اولین ماشه لامبدا تنظیم می کند. اکنون می توانیم تمام اقدامات را از تابع لامبدا اصلی خود انجام دهیم. کد نهایی با تمام توابع به صورت زیر خواهد بود:

module.exports.preAuthTrigger = async (event) => {
    if (!(await this.isUserEnabled(event))) throw new Error('Usuário Bloqueado')

    const attempts = await this.getLoginAttempts(event)
    if (attempts > 4) {
        await this.disableUser(event)
        throw new Error('Usuário Bloqueado')
    }

    await this.updateLoginAttempts(event, attempts)
    return event
}

exports.isUserEnabled = async (event) => {
    const getParams = {
        UserPoolId: event.userPoolId,
        Username: event.userName,
    }
    const userData = await cognitoService.adminGetUser(getParams).promise()
    return userData.Enabled
}

exports.getLoginAttempts = async (event) => {
    const getParams = {
        UserPoolId: event.userPoolId,
        Username: event.userName,
    }
    const userData = await cognitoService.adminGetUser(getParams).promise()
    const attribute = userData.UserAttributes.find(
        (att) => att.Name === 'custom:login_attempts'
    )
    if (attribute !== undefined && attribute !== null)
        return parseInt(attribute.Value)
    else return 0
}


exports.disableUser = async (event) => {
    await cognitoService
        .adminDisableUser({
            UserPoolId: event.userPoolId,
            Username: event.userName,
        })
        .promise()
}


exports.updateLoginAttempts = async (event, attempts) => {
    const updateParams = {
        UserAttributes: [
            {
                Name: 'custom:login_attempts',
                Value: (attempts + 1).toString(),
            },
        ],
        UserPoolId: event.userPoolId,
        Username: event.userName,
    }

    await cognitoService.adminUpdateUserAttributes(updateParams).promise()
}
وارد حالت تمام صفحه شوید

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

در پست بعدی من کد تریگر PostAuth lambda را می نویسیم و خواهیم دید که چگونه می توانیم cognito را برای استفاده از هر دو لامبدا تنظیم کنیم!

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

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

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

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