Azure AD B2C自定义用户属性

Azure AD B2C Custom User Attributes

我是Azure B2C世界的新手。我正在尝试创建"自定义用户"属性来存储我们应用程序的数据。我已经在Azure门户中创建了它,并将其分配给了我的"注册/登录"策略。但是,我希望能够以编程方式更新/读取此值。我一直在使用Graph API和注册Extensions。有两个问题:

1)扩展名/自定义属性是否相同?
2)我尝试过此代码,并且返回的扩展名始终为空:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 public void RegisterExtension()
    {
        string myRegisteredAppObjectId ="<>";
        string json = @"{
                       ""name"":""My Custom Attribute"",
                       ""dataType"":""String"",
                       ""targetObjects"": [
                           ""User""
                        ]
                        }";

        B2CGraphClient b2CGraphClient = new B2CGraphClient();
        b2CGraphClient.RegisterExtension(myRegisteredAppObjectId, json);
        var extensions = JsonConvert.DeserializeObject(b2CGraphClient.GetExtensions(myRegisteredAppObjectId).Result);

    }

B2CGraphClient.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
 public class B2CGraphClient
{
    private string clientId { get; set; }
    private string clientSecret { get; set; }
    private string tenant { get; set; }

    private AuthenticationContext authContext;
    private ClientCredential credential;

    public B2CGraphClient(string clientId, string clientSecret, string tenant)
    {
        // The client_id, client_secret, and tenant are pulled in from the App.config file
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.tenant = tenant;

        // The AuthenticationContext is ADAL's primary class, in which you indicate the direcotry to use.
        this.authContext = new AuthenticationContext("https://login.microsoftonline.com/" + tenant);

        // The ClientCredential is where you pass in your client_id and client_secret, which are
        // provided to Azure AD in order to receive an access_token using the app's identity.
        this.credential = new ClientCredential(clientId, clientSecret);
    }


    public async Task<string> DeleteUser(string objectId)
    {
        return await SendGraphDeleteRequest("/users/" + objectId);
    }

    public async Task<string> RegisterExtension(string objectId, string body)
    {
        return await SendGraphPostRequest("/applications/" + objectId +"/extensionProperties", body);
    }


    public async Task<string> GetExtensions(string appObjectId)
    {
        return await SendGraphGetRequest("/applications/" + appObjectId +"/extensionProperties", null);
    }


    private async Task<string> SendGraphPostRequest(string api, string json)
    {
        // NOTE: This client uses ADAL v2, not ADAL v4
        AuthenticationResult result = authContext.AcquireToken(Globals.aadGraphResourceId, credential);
        HttpClient http = new HttpClient();
        string url = Globals.aadGraphEndpoint + tenant + api +"?" + Globals.aadGraphVersion;

        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.WriteLine("POST" + url);
        Console.WriteLine("Authorization: Bearer" + result.AccessToken.Substring(0, 80) +"...");
        Console.WriteLine("Content-Type: application/json");
        Console.WriteLine("");
        Console.WriteLine(json);
        Console.WriteLine("");

        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
        request.Content = new StringContent(json, Encoding.UTF8,"application/json");
        HttpResponseMessage response = await http.SendAsync(request);

        if (!response.IsSuccessStatusCode)
        {
            string error = await response.Content.ReadAsStringAsync();
            object formatted = JsonConvert.DeserializeObject(error);
            throw new WebException("Error Calling the Graph API: \
" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
        }

        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine((int)response.StatusCode +":" + response.ReasonPhrase);
        Console.WriteLine("");

        return await response.Content.ReadAsStringAsync();
    }

    public async Task<string> SendGraphGetRequest(string api, string query)
    {
        // First, use ADAL to acquire a token using the app's identity (the credential)
        // The first parameter is the resource we want an access_token for; in this case, the Graph API.
        AuthenticationResult result = authContext.AcquireToken("https://graph.windows.net", credential);

        // For B2C user managment, be sure to use the 1.6 Graph API version.
        HttpClient http = new HttpClient();
        string url ="https://graph.windows.net/" + tenant + api +"?" + Globals.aadGraphVersion;
        if (!string.IsNullOrEmpty(query))
        {
            url +="&" + query;
        }

        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.WriteLine("GET" + url);
        Console.WriteLine("Authorization: Bearer" + result.AccessToken.Substring(0, 80) +"...");
        Console.WriteLine("");

        // Append the access token for the Graph API to the Authorization header of the request, using the Bearer scheme.
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
        HttpResponseMessage response = await http.SendAsync(request);

        if (!response.IsSuccessStatusCode)
        {
            string error = await response.Content.ReadAsStringAsync();
            object formatted = JsonConvert.DeserializeObject(error);
            throw new WebException("Error Calling the Graph API: \
" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
        }

        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine((int)response.StatusCode +":" + response.ReasonPhrase);
        Console.WriteLine("");

        return await response.Content.ReadAsStringAsync();
    }
}

当然,myRegisteredAppObjectId中具有有效的GUID。

谢谢


Are extensions/custom attributes the same thing?

根据我的测试,扩展名与自定义属性相同。

I've tried this code and the returned extensions are always empty:

我在本教程之后添加了自定义属性MyCustomAttribute,并在我的策略中使用了自定义属性。您可以参考我的测试步骤。

我从Github下载B2C-GraphAPI-DotNet项目。将以下代码用于自定义属性

1
2
3
var applications = client.GetApplications("$filter=startswith(displayName, 'b2c-extensions-app')").Result

var extension = client.GetExtensions(objectId).Result //objectId from the applications result.

然后我们可以从扩展名中获取自定义属性。

enter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var jsonObject = new JObject
            {
                {"accountEnabled", true},
                {"country","US"},
                {"creationType","LocalAccount"},
                {"displayName","Tomsun"},
                {"passwordPolicies","DisablePasswordExpiration,DisableStrongPassword"},
                {"extension_42ba0de8530a4b5bbe6dad21fe6ef092_MyCustomAttribute","test2"},  //custom propery
                {"passwordProfile", new JObject
                {
                    {"password","!QAZ1234wer"},
                    {"forceChangePasswordNextLogin", true}
                } },
                {"signInNames", new JArray
                    {
                        new JObject
                        {
                            {"value","[email protected]"},
                            {"type","emailAddress"}
                        }
                    }
                }
            };

string user = client.CreateUser(jsonObject.ToString()).Result;

查询用户

1
var user = client.GetUserByObjectId(objectId).Result; //user objectId

enter

1
2
3
4
5
6
var jsonUpdate = new JObject
            {  
                {"extension_42ba0de8530a4b5bbe6dad21fe6ef092_MyCustomAttribute","testx"}

            };
var updateuser = client.UpdateUser("objectId", jsonObject2.ToString()).Result; //UserObject Id