Giter Site home page Giter Site logo

b2c-approles's People

Contributors

cljung avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

b2c-approles's Issues

Can't Login after using Custom policy Signup Signin.

I using this guide to setup Azure Ad B2c. After that I this guide to custom policy to use RBAC.

BUT I got an issue is: I use postman to get token

img1

Auth url: https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/oauth2/v2.0/authorize?p=b2c_1a_ft_signup_signin

Token Url: https://dynatexdigital.b2clogin.com/dynatexdigital.onmicrosoft.com/oauth2/v2.0/toeken?p=b2c_1a_ft_signup_signin

My allication in Azure AD B2c:

enter image description here

This is my authentication setting in MyWebApp
enter image description here

When I Signup and Get Token, The response doesn't contain accesstoken:

enter image description here

My Custom policy:

https://github.com/wangyiwu/AzureAdPolicy

Error are:

  1. I can't get access token when authentication with postman
  2. I can't login, the second time when I Login by postman or run in Identity Experience Framework the message is" Username or passsword invalid. I have tried reset password in Azure|User i got message "password expired".

enter image description here
enter image description here

SignIN link not redirecting user to AzureAD B2C login page

Hi Christer,

Thanks for writing excellent article for delegated admin and RBAC for Azure AD B2C. It has helped us a lot to understand how platform works. We are currently trying to setup Delegated admin poc but the code you have shared here in the github is missing AccountController.cs file. We are not able to redirected to our Azure AD B2C login page once we click on SignIn link of the application running locally from VisualStudio2019.
Can you please update your code in the repository so that we can run it locally? Please note that I am new to .net platfrom.

Looking forward for your reply.

Best Regards from Oslo,
Sachin Dave

Unauthorized when run Azure Function App?

I have a Azure Function app, I create A Function:

#r "Newtonsoft.Json"

using System.Net;
using System.Text;
using Newtonsoft.Json;
using  Newtonsoft.Json.Linq;

public static async Task<HttpResponseMessage> Run(HttpRequest req, ILogger log)
{
    log.LogInformation("C# HTTP trigger function processed a request.");

    string objectId = req.Query["objectId"]; // User
    string clientId = req.Query["clientId"]; // App
    string tenantId = req.Query["tenantId"]; // Tenant
    string scope = req.Query["scope"];       // "roles", "roles groups" or "groups"

    log.LogInformation("objectId: " + objectId);
    
    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    dynamic data = JsonConvert.DeserializeObject(requestBody);
    objectId = objectId ?? data?.objectId;
    clientId = clientId ?? data?.clientId;
    tenantId = tenantId ?? data?.tenantId;
    scope = scope ?? data?.scope;
    // default just roles
    if ( string.IsNullOrWhiteSpace(scope) ) {
        scope = "roles";
    } else {
        scope = scope.ToLowerInvariant();
    }
    log.LogInformation($"Params: objectId={objectId}, clientId={clientId}, tenantId={tenantId}, scope={scope}" );

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    // If you use Cert based auth, you will receive a client cert
    //////////////////////////////////////////////////////////////////////////////////////////////////////
    var cert = req.HttpContext.Connection.ClientCertificate;
    log.LogInformation($"Incoming cert: {cert}");
    if(cert != null ) { 
        var b2cCertSubject = System.Environment.GetEnvironmentVariable( $"B2C_{tenantId}_CertSubject"); //
        var b2cCertThumbprint = System.Environment.GetEnvironmentVariable($"B2C_{tenantId}_CertThumbprint");
        if ( !( cert.Subject.Equals(b2cCertSubject) && cert.Thumbprint.Equals(b2cCertThumbprint) ) ) {
            var respContent = new { version = "1.0.0", status = (int)HttpStatusCode.BadRequest, 
                                    userMessage = "Technical error - cert..."};
            var json = JsonConvert.SerializeObject(respContent);
            log.LogInformation(json);
            return new HttpResponseMessage(HttpStatusCode.Conflict) {
                            Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json")
            };
        }
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    // get the B2C client credentials for this tenant
    //////////////////////////////////////////////////////////////////////////////////////////////////////
    var b2cClientId = System.Environment.GetEnvironmentVariable($"B2C_{tenantId}_ClientId"); //
    var b2cClientSecret = System.Environment.GetEnvironmentVariable($"B2C_{tenantId}_ClientSecret");
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////
    // Authenticate via the Client Credentials flow
    //////////////////////////////////////////////////////////////////////////////////////////////////////
    string accessToken = GetCachedAccessToken( tenantId );
    log.LogInformation($"accessToken: {accessToken}");
    if ( null == accessToken ) 
    {
        HttpClient client = new HttpClient();
        var dict= new Dictionary<string, string>();
        dict.Add("grant_type", "client_credentials");
        dict.Add("client_id", b2cClientId);
        dict.Add("client_secret", b2cClientSecret);
        dict.Add("resource", "https://graph.microsoft.com");
        dict.Add("scope", "User.Read.All AppRoleAssignment.Read.All");

        var urlTokenEndpoint = $"https://login.microsoftonline.com/{tenantId}/oauth2/token?api-version=1.0";
        log.LogInformation(urlTokenEndpoint);

        HttpResponseMessage resp = client.PostAsync( urlTokenEndpoint, new FormUrlEncodedContent(dict)).Result;
        var contents = await resp.Content.ReadAsStringAsync();
        client.Dispose();
        log.LogInformation("HttpStatusCode=" + resp.StatusCode.ToString() + " - " + contents );

        // If the client creds failed, return error
        if ( resp.StatusCode != HttpStatusCode.OK ) {
            var respContent = new { version = "1.0.0", status = (int)HttpStatusCode.BadRequest, userMessage = "Technical error..."};
            var json = JsonConvert.SerializeObject(respContent);
            log.LogInformation(json);
            return new HttpResponseMessage(HttpStatusCode.Conflict) {
                            Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json")
            };
        }
        accessToken = JObject.Parse(contents)["access_token"].ToString();
        CacheAccessToken( tenantId, accessToken );
    }
    log.LogInformation(accessToken);

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    // GraphAPI query for user's group membership
    //////////////////////////////////////////////////////////////////////////////////////////////////////
    var groupsList = new List<string>();
    if ( scope.Contains("groups") ) {
        HttpClient httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken );
        var url = $"https://graph.microsoft.com/v1.0/users/{objectId}/memberOf?$select=id,displayName";
        log.LogInformation(url);
        var res = await httpClient.GetAsync(url);
        log.LogInformation("HttpStatusCode=" + res.StatusCode.ToString());
        if(res.IsSuccessStatusCode) {
            var respData = await res.Content.ReadAsStringAsync();
            var groupArray = (JArray)JObject.Parse(respData)["value"];
            foreach (JObject g in groupArray) {
                var name = g["displayName"].Value<string>();
                groupsList.Add(name);
            }    
        }
        httpClient.Dispose();
    }

    //////////////////////////////////////////////////////////////////////////////////////////////////////
    // GraphAPI query for user AppRoleAssignments
    //////////////////////////////////////////////////////////////////////////////////////////////////////
    JObject userData = null;
    bool hasAssignments = false;
    var roleNames = new List<string>();

    if ( scope.Contains("roles") ) {
        HttpClient httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken );
        var url = $"https://graph.microsoft.com/beta/users/{objectId}/appRoleAssignments?$select=appRoleId,resourceId,resourceDisplayName";
        log.LogInformation(url);
        var res = await httpClient.GetAsync(url);
        log.LogInformation("HttpStatusCode=" + res.StatusCode.ToString());
        if(res.IsSuccessStatusCode) {
            var respData = await res.Content.ReadAsStringAsync();
            log.LogInformation(respData);
            userData = JObject.Parse(respData);
            foreach( var item in userData["value"] ) {
                hasAssignments = true;
                break;
            }
        }
        httpClient.Dispose();
    }

    if ( hasAssignments ) {
        HttpClient httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
        var url = $"https://graph.microsoft.com/beta/servicePrincipals?$filter=appId eq '{clientId}'&$select=id,appRoles";
        log.LogInformation(url);
        var res = httpClient.GetAsync(url).Result;
        JArray appRolesArray = null;
        string spId = "";
        if (res.IsSuccessStatusCode) {
            var respData = res.Content.ReadAsStringAsync().Result;
            var spoData = JObject.Parse(respData);
            foreach (var item in spoData["value"]) {
                spId = item["id"].ToString();
                appRolesArray = (JArray)item["appRoles"];
                foreach (var itemUser in userData["value"]) {
                    if (spId == itemUser["resourceId"].ToString()) {
                        var appRoleId = itemUser["appRoleId"].ToString();
                        foreach (var role in appRolesArray) {
                            if (appRoleId == role["id"].ToString()) {
                                roleNames.Add(role["value"].ToString());
                            }
                        }
                    }
                }
            }
        }
        httpClient.Dispose();
    }

    var jsonToReturn = JsonConvert.SerializeObject( new { roles = roleNames, groups = groupsList } );
    log.LogInformation(jsonToReturn);
    return new HttpResponseMessage(HttpStatusCode.OK) {
            Content = new StringContent(jsonToReturn, System.Text.Encoding.UTF8, "application/json")
        };
}

public static string GetCachedAccessToken( string tenantId, int secondsRemaining = 60 ) // access_token needs to be valid for N seconds more
{
    string accessToken = Environment.GetEnvironmentVariable($"B2C_{tenantId}_AppRoles_AccessToken");
    
    if (accessToken != null)
    {
        DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        string b64 = accessToken.Split(".")[1];
        while ((b64.Length % 4) != 0)
            b64 += "=";
        JObject jwtClaims = JObject.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(b64)));
        DateTime expiryTime = epoch.AddSeconds(int.Parse(jwtClaims["exp"].ToString()));
        if (DateTime.UtcNow >= expiryTime.AddSeconds(-secondsRemaining))
            accessToken = null; // invalidate
    }
    return accessToken;
}
public static void CacheAccessToken( string tenantId, string accesToken )
{
    Environment.SetEnvironmentVariable($"B2C_{tenantId}_AppRoles_AccessToken", accesToken);
}

This is function.json

  "bindings": [
    {
      "authLevel": "function",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    }
  ]
}

When I Run, I get Logs:

Connected!
2022-06-15T08:40:52.461 [Information] Executing 'Functions.GetAppRoleAssignmentsMSGraph' (Reason='This function was programmatically called via the host APIs.', Id=b0dec087-1c3a-4140-94b8-abf442845793)
2022-06-15T08:40:52.461 [Information] C# HTTP trigger function processed a request.
2022-06-15T08:40:52.461 [Information] objectId: 76ae8450-37ee-4cec-b27e-edffe607fd08
2022-06-15T08:40:52.461 [Information] Params: objectId=76ae8450-37ee-4cec-b27e-edffe607fd08, clientId=95155f6c-a52b-4181-ad39-45e89701de6d, tenantId=7d73c792-46b8-402b-b77f-38ce079a3dd7", scope=roles
2022-06-15T08:40:52.461 [Information] Incoming cert:
2022-06-15T08:40:52.462 [Information] accessToken:
2022-06-15T08:40:52.462 [Information] https://login.microsoftonline.com/7d73c792-46b8-402b-b77f-38ce079a3dd7"/oauth2/token?api-version=1.0
2022-06-15T08:40:52.681 [Information] HttpStatusCode=Unauthorized - {"error":"invalid_client","error_description":"AADSTS7000216: 'client_assertion', 'client_secret' or 'request' is required for the 'client_credentials' grant type.\r\nTrace ID: 396474fe-7af3-4b2f-ad01-44283acb2b00\r\nCorrelation ID: 03c73d62-7776-4f2c-b2b3-c2b0c7457098\r\nTimestamp: 2022-06-15 08:40:52Z","error_codes":[7000216],"timestamp":"2022-06-15 08:40:52Z","trace_id":"396474fe-7af3-4b2f-ad01-44283acb2b00","correlation_id":"03c73d62-7776-4f2c-b2b3-c2b0c7457098","error_uri":"https://login.microsoftonline.com/error?code=7000216"}
2022-06-15T08:40:52.681 [Information] {"version":"1.0.0","status":400,"userMessage":"Technical error..."}
2022-06-15T08:40:52.681 [Information] Executed 'Functions.GetAppRoleAssignmentsMSGraph' (Succeeded, Id=b0dec087-1c3a-4140-94b8-abf442845793, Duration=220ms)
2022-06-15T08:42:13  No new trace in the past 1 min(s).

This is my Query

![anh](https://i.imgur.com/0d58L4d.png)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.