עבודה עם היקפי הרשאות OAuth2

הדף הזה רלוונטי ל-Apigee ול-Apigee Hybrid.

לעיון במסמכי התיעוד של Apigee Edge

במאמר הזה נסביר איך להשתמש בהיקפי הרשאות של OAuth 2.0 ולאכוף אותם ב-Apigee.

מהו היקף הרשאות OAuth2?

היקפי OAuth 2.0 מאפשרים להגביל את רמת הגישה שניתנת לטוקן גישה. לדוגמה, יכול להיות שאסימון גישה שהונפק לאפליקציית לקוח יקבל הרשאות קריאה וכתיבה למשאבים מוגנים, או רק הרשאות קריאה. אתם יכולים להטמיע את ממשקי ה-API כדי לאכוף כל היקף או שילוב של היקפים שתרצו. לכן, אם לקוח מקבל אסימון עם הרשאת קריאה (READ) ומנסה להתקשר לנקודת קצה ל-API שנדרשת לה גישת כתיבה (WRITE), הקריאה תיכשל.

בנושא הזה נסביר איך מוקצים היקפי הרשאות לאסימוני גישה ואיך Apigee אוכף היקפי הרשאות של OAuth 2.0. אחרי שתקראו את הנושא הזה, תוכלו להשתמש בהיקפי הרשאות בביטחון.

איך מוקצים היקפים לאסימוני גישה?

כש-Apigee יוצר אסימון גישה, הוא עשוי להקצות לו היקף. כדי להבין איך זה קורה, צריך קודם להכיר את הישויות הבאות ב-Apigee: מוצרי API, מפתחים ואפליקציות למפתחים. למבוא, ראו מבוא לפרסום. מומלץ לעיין בחומר הזה לפני שממשיכים.

אסימון גישה הוא מחרוזת ארוכה של תווים שנראים אקראיים, שמאפשרת ל-Apigee לאמת בקשות API נכנסות (אפשר לחשוב על זה כעל תחליף לפרטי כניסה רגילים של שם משתמש וסיסמה). מבחינה טכנית, האסימון הוא מפתח שמפנה לאוסף של מטא-נתונים שנראה כך:

{
  "issued_at" : "1416962591727",
  "application_name" : "0d3e1d41-a59f-4d74-957e-d4e3275d4781",
  "scope" : "A",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-bs0cSuqS9y]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-AdBmANhsag@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "eTtB7w5lvk3DnOZNGReBlvGvIAeAywun",
  "access_token" : "ODm47ris5AlEty8TDc1itwYPe5MW",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

המטא-נתונים של האסימון כוללים את המחרוזת של אסימון הגישה בפועל, פרטי התפוגה, זיהוי של אפליקציית המפתח, המפתח והמוצרים שמשויכים לאסימון. תוכלו לראות שהמטא-נתונים כוללים גם את 'היקף'.

איך הטוקן מקבל את ההיקף שלו?

הדבר הראשון שחשוב לזכור כדי להבין את ההיקף הוא שלכל מוצר באפליקציה של מפתח יכולים להיות אפס או יותר היקפים שמשויכים אליו. אפשר להקצות את ההיקפים האלה כשיוצרים את המוצר, או להוסיף אותם מאוחר יותר. הם מופיעים כרשימה של שמות ונכללים ב'מטא-נתונים' שמשויכים לכל מוצר.

כשיוצרים אפליקציה למפתחים ומוסיפים לה מוצרים, מערכת Apigee בודקת את כל המוצרים באפליקציה למפתחים ויוצרת רשימה של כל ההיקפים של המוצרים האלה (רשימת ההיקפים הראשית או הגלובלית של האפליקציה – איחוד של כל ההיקפים המוכרים).

כשלקוח אפליקציה מבקש אסימון גישה מ-Apigee, הוא יכול לציין אילו היקפי הרשאות הוא רוצה לשייך לאסימון הזה. לדוגמה, בבקשה הבאה מוגדר היקף ההרשאה 'A'. כלומר, הלקוח מבקש משרת ההרשאות (Apigee) ליצור אסימון גישה עם היקף הרשאות A (שנותן לאפליקציה הרשאה להפעיל ממשקי API עם היקף הרשאות A). האפליקציה שולחת בקשת POST כזו:

curl -i -X POST -H Authorization: Basic Mg12YTk2UkEIyIBCrtro1QpIG \
  -H content-type:application/x-www-form-urlencoded \
  https://apitest.acme.com/oauth/token?grant_type=client_credentials&scope=A

מה קורה

כש-Apigee מקבל את הבקשה הזו, הוא יודע איזו אפליקציה שולחת את הבקשה ואיזו אפליקציית פיתוח הלקוח רשם (מזהה הלקוח ומפתחות הסוד של הלקוח מקודדים בכותרת של אימות בסיסי). מכיוון שפרמטר השאילתה scope כלול, מערכת Apigee צריכה להחליט אם לאחד ממוצרי ה-API שמשויכים לאפליקציית המפתח יש היקף 'A'. אם כן, נוצר אסימון גישה עם ההיקף 'A'. דרך נוספת להסתכל על זה היא שפרמטר השאילתה scope הוא סוג של מסנן. אם אפליקציית המפתחים מזהה את ההיקפים A,‏ B ו-X, ופרמטר השאילתה מציין scope=X Y Z, רק ההיקף X יוקצה לטוקן.

מה קורה אם הלקוח לא מצרף פרמטר של היקף? במקרה כזה, Apigee יוצר טוקן שכולל את כל ההיקפים שהאפליקציה למפתחים מזהה. חשוב להבין שההתנהגות שמוגדרת כברירת מחדל היא החזרת טוקן גישה שמכיל את איחוד כל ההיקפים של כל המוצרים שנכללים באפליקציה למפתחים.

אם לא צוינו היקפי הרשאות לאף אחד מהמוצרים שמשויכים לאפליקציה של המפתח, אבל לאסימון כן צוין היקף הרשאות, הקריאות שמתבצעות באמצעות האסימון הזה ייכשלו.

נניח שאפליקציית מפתח מזהה את היקפי ההרשאות הבאים: A B C D. זו רשימת ההיקפים הראשית של האפליקציה. יכול להיות שלמוצר אחד באפליקציה יש היקף A ו-B, ולמוצר שני יש היקף C ו-D, או כל שילוב אחר. אם הלקוח לא מציין פרמטר scope (או אם הוא מציין פרמטר של היקף ללא ערך), האסימון יקבל את כל ארבעת ההיקפים: A, ‏ B,‏ C ו-D. שוב, האסימון מקבל קבוצה של היקפי הרשאה שהיא איחוד של כל היקפי ההרשאה שהאפליקציה של המפתח מזהה.

יש עוד מקרה שבו התנהגות ברירת המחדל היא להחזיר אסימון גישה עם כל ההיקפים המוכרים, והוא כשבמדיניות GenerateAccessToken (מדיניות Apigee שמפיקה אסימוני גישה) לא מצוין רכיב <Scope>. לדוגמה, הנה מדיניות GenerateAccessToken שבה <Scope> is מוגדר. אם רכיב <Scope> חסר (או אם הוא קיים אבל ריק), תתבצע ההתנהגות שמוגדרת כברירת מחדל.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-GenerateAccessToken">
    <DisplayName>OAuthV2 - Generate Access Token</DisplayName>
    <Attributes>
      <Attribute name='hello' ref='system.time' display='false'>value1</Attribute>
    </Attributes>
    <Scope>request.queryparam.scope</Scope> 
    <GrantType>request.formparam.grant_type</GrantType>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>GenerateAccessToken</Operation>
    <SupportedGrantTypes>
      <GrantType>client_credentials</GrantType>
    </SupportedGrantTypes>
  <GenerateResponse enabled="true"/>
</OAuthV2>

איך נאכפים טווחי ההרשאות?

קודם כל, חשוב לזכור שב-Apigee, אסימוני גישה מאומתים באמצעות מדיניות OAuthV2 (בדרך כלל ממוקמת בתחילת זרימת הנתונים של שרת proxy). במדיניות צריך לציין את הפעולה VerifyAccessToken. הנה המדיניות:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope> <!-- Optional: space-separated list of scope names. -->
    <GenerateResponse enabled="true"/>
</OAuthV2>

שימו לב לרכיב <Scope>. הוא משמש לציון ההיקפים שהמדיניות תקבל.

בדוגמה הזו, המדיניות תצליח רק אם אסימון הגישה כולל את ההיקף A. אם הרכיב <Scope> מושמט או אם אין לו ערך, המדיניות מתעלמת מההיקף של אסימון הגישה.

עכשיו, עם היכולת לאמת אסימוני גישה על סמך היקף ההרשאות, אתם יכולים לתכנן את ממשקי ה-API כך שיאכפו היקפי הרשאות ספציפיים. כדי לעשות את זה, צריך לעצב תהליכים מותאמים אישית עם מדיניות VerifyAccessToken מודעת-היקף שמצורפת אליהם.

נניח של-API יש זרימה שמוגדרת לנקודת הקצה /resourceA:

<Flow name="resourceA">
            <Condition>(proxy.pathsuffix MatchesPath "/resourceA") and (request.verb = "GET")</Condition>
            <Description>Get a resource A</Description>
            <Request>
                <Step>
                    <Name>OAuthV2-VerifyAccessTokenA</Name>
                </Step>
            </Request>
            <Response>
                <Step>
                    <Name>AssignMessage-CreateResponse</Name>
                </Step>
            </Response>
        </Flow>

כשתהליך העבודה הזה מופעל (מתקבלת בקשה עם /resourceA בסיומת של נתיב), המדיניות OAuthV2-VerifyAccessTokenA מופעלת באופן מיידי. המדיניות הזו בודקת שאסימון הגישה תקף, ומחפשת את ההיקפים שהאסימון תומך בהם. אם המדיניות מוגדרת כמו בדוגמה שלמטה, עם <Scope>A</Scope>, המדיניות תפעל רק אם לטוקן הגישה יש היקף A. אחרת, תוחזר שגיאה.

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

לסיכום, מפתחי API אחראים לעיצוב של אכיפת היקף ההרשאות בממשקי ה-API שלהם. הם עושים את זה על ידי יצירת תהליכים מותאמים אישית לטיפול בהיקפים ספציפיים, וצירוף מדיניות VerifyAccessToken כדי לאכוף את ההיקפים האלה.

דוגמאות לקוד

לבסוף, נבחן כמה קריאות לדוגמה ל-API כדי להמחיש איך טוקנים מקבלים היקפי הרשאה ואיך היקפי הרשאה נאכפים.

מקרה ברירת מחדל

נניח שיש לכם אפליקציה למפתחים עם מוצרים, ואיחוד ההיקפים של המוצרים האלה הוא: A,‏ B ו-C. קריאה ל-API זו מבקשת טוקן גישה, אבל לא מציינת פרמטר של שאילתת היקף.

curl -X POST -H content-type:application/x-www-form-urlencoded \
  https://apitest.acme.com/scopecheck1/token?grant_type=client_credentials

במקרה כזה, לטוקן שנוצר יוקצו ההיקפים א', ב' ו-ג' (התנהגות ברירת המחדל). המטא-נתונים של האסימון ייראו בערך כך:

{
  "issued_at" : "1417016208588",
  "application_name" : "eb1a0333-5775-4116-9eb2-c36075ddc360",
  "scope" : "A B C",
  "status" : "approved",
  "api_product_list" : "[scopecheck1-yEgQbQqjRR]",
  "expires_in" : "1799", //--in seconds
  "developer.email" : "scopecheck1-yxiuHuZcDW@apigee.com",
  "organization_id" : "0",
  "token_type" : "BearerToken",
  "client_id" : "atGFvl3jgA0pJd05rXKHeNAC69naDmpW",
  "access_token" : "MveXpj4UYXol38thNoJYIa8fBGlI",
  "organization_name" : "wwitman",
  "refresh_token_expires_in" : "0", //--in seconds
  "refresh_count" : "0"
}

נניח שיש לכם נקודת קצה ל-API עם היקף 'A' (כלומר, היא VerifyAccessToken דורשת היקף 'A'). זו המדיניות VerifyAccessToken:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenA">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

הנה דוגמה לקריאה לנקודת קצה שמחייבת את היקף A:

curl -X GET -H Authorization: Bearer MveXpj4UYXol38thNoJYIa8fBGlI \
  https://apitest.acme.com/scopecheck1/resourceA

קריאת ה-GET הזו מסתיימת בהצלחה:

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }

הפעולה מצליחה כי מדיניות VerifyAccessToken שמופעלת כשקוראים לנקודת הקצה דורשת היקף A, ואסימון הגישה קיבל היקפים A,‏ B ו-C – התנהגות ברירת המחדל.

סינון בקשות תמיכה

נניח שיש לכם אפליקציה למפתחים עם מוצרים שיש להם היקפי הרשאות A,‏ B,‏ C ו-X. שולחים בקשה לאסימון גישה וכוללים את פרמטר השאילתה scope, כך:

curl -i -X POST -H content-type:application/x-www-form-urlencoded \
  'https://apitest.acme.com/oauth/token?grant_type=client_credentials&scope=A X'

במקרה כזה, לטוקן שנוצר יוקצו ההיקפים A ו-X, כי שניהם היקפים תקפים. חשוב לזכור שאפליקציית המפתח מזהה את היקפי ההרשאות A,‏ B,‏ C ו-X. במקרה כזה, אתם מסננים את רשימת מוצרי ה-API על סמך ההיקפים האלה. אם למוצר יש היקף A או X, אפשר להגדיר נקודות קצה של API שיאכפו את ההיקפים האלה. אם למוצר אין היקף A או X (נניח שיש לו היקפים B,‏ C ו-Z), אי אפשר לבצע קריאה ל-API שדורש היקפים A או X באמצעות הטוקן.

כשמפעילים את ה-API עם הטוקן החדש:

curl -X GET -H Authorization: Bearer Rkmqo2UkEIyIBCrtro1QpIG \
  https://apitest.acme.com/scopecheck1/resourceX

אסימון הגישה מאומת על ידי proxy ל-API. לדוגמה:

<OAuthV2 async="false" continueOnError="false" enabled="true" name="OAuthV2-VerifyAccessTokenX">
    <DisplayName>Verify OAuth v2.0 Access Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <Scope>A X</Scope>
    <GenerateResponse enabled="true"/>
</OAuthV2>

הפעלת ה-GET מצליחה ומוחזרת תשובה. לדוגמה:

 {
   "hello" : "Tue, 25 Nov 2014 01:35:53 UTC"
 }
 

הבקשה מצליחה כי מדיניות VerifyAccessToken דורשת את היקף A או X, ואסימון הגישה כולל את היקף A ואת היקף X. כמובן, אם הרכיב <Scope> היה מוגדר ל-B, השיחה הזו הייתה נכשלת.

סיכום

חשוב להבין איך Apigee מטפל בהיקפי הרשאות של OAuth 2.0. הנה כמה נקודות מרכזיות:

  • אפליקציה של מפתח 'מזהה' את האיחוד של כל ההיקפים שהוגדרו לכל המוצרים שלה.
  • כשאפליקציה מבקשת אסימון גישה, היא יכולה לציין אילו היקפי הרשאות היא רוצה לקבל. מערכת Apigee (שרת ההרשאות) קובעת אילו היקפי הרשאות יוקצו בפועל לאסימון הגישה, על סמך (א) היקפי ההרשאות שנדרשים ו-(ב) היקפי ההרשאות שמזוהים על ידי אפליקציית המפתחים.
  • אם Apigee לא מוגדר לבדיקת היקף (הרכיב <Scope> חסר במדיניות VerifyAccessToken או שהוא ריק), קריאה ל-API תצליח כל עוד ההיקף שמוטמע בטוקן הגישה תואם לאחד ההיקפים שמזוהים על ידי אפליקציית המפתחים הרשומה (אחד ההיקפים ברשימת ההיקפים 'הראשית' של האפליקציה).
  • אם לטוקן גישה לא משויכים היקפי הרשאה, הוא יצליח רק במקרים שבהם Apigee לא מתייחס להיקף ההרשאה (הרכיב <Scope> חסר במדיניות VerifyAccessToken או שהוא ריק).