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

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

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

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

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

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

בנושא הזה נסביר איך מוקצים היקפי הרשאות לאסימוני גישה ואיך 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"
}

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

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

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

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

כשלקוח אפליקציה מבקש אסימון גישה מ-Apigee, הוא יכול לציין אילו היקפי הרשאות הוא רוצה לשייך לאסימון הזה. לדוגמה, בבקשה הבאה מוגדר היקף ההרשאה 'A'. כלומר, הלקוח מבקש משרת ההרשאות (Apigee) ליצור אסימון גישה עם היקף 'א' (שמעניק לאפליקציה הרשאה להפעיל ממשקי API עם היקף 'א'). האפליקציה שולחת בקשת 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> . אם רכיב <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

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

{
  "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 או שהוא ריק).