Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to login to a Azure Active Directory as a User in Test-Code?

I'm new to Azure and struggle a little in learning all the functionalities of the Azure Active Directory (AAD), so I hope you can clear some things up for me. Here is what I already did:

  • I registered a web app which serves as a resource provider and offers different APIs behind a API management service.

  • The web app has several users and roles in the AAD. Plus, more detailed permissions are set on App-Level. So the AAD doesn't control all permissions of my users.

  • Users are authenticated by using OAuth 2.0. In practice, this means if a new user tries to login to my app he gets redirected to Microsofts login page, enters username and password and then gets a JWT token from Microsofts authentication server.

Now what I want to do:

I want to write an app running on my build server which tests the user permissions. The app has to be written in C# .NET Core. Now I'm struggling on how to log in as a user from my code, so my question is:

How can i log in as a user from code to AAD and get the JWT token to test the user permissions? Can I do this by just using username / password, or do I need to register my test app in the AAD? What are the best solutions to reach my goals?

Thank you in advance

like image 518
Tobias von Falkenhayn Avatar asked Oct 19 '25 21:10

Tobias von Falkenhayn


2 Answers

Juunas' comment already covered most of what is required. Just putting a bit more detail behind it.

  • You can use MSAL (link) to write a .NET Core application that accesses your API.
  • Within MSAL, you need to use username password authentication (Resource Owner Password Credentials grant) to acquire a JWT token. Please never use this grant outside your testing application.
  • Depending on how your app is configured, using just the clientId of the API could be enough. It would however be best practice to register a separate native app.

Some wording to help you along:

  • ClientId: The id of the client application which is requesting the token.
  • Scope: The scope of the API you acquire the token for. Should already be configured somewhere in your API. Usually something with the AppId URI. Possible examples could look like:
    • https://<yourtenant>.onmicrosoft.com/<yourapi>/user_impersonation
    • https://<clientId-of-API>/.default
    • ...
  • Authority: Your AAD, e.g. https://login.microsoftonline.com/yourtenant.onmicrosoft.com

Code example for the password grant from the wiki (more examples there):

static async Task GetATokenForGraph()
{
    string authority = "https://login.microsoftonline.com/contoso.com";
    string[] scopes = new string[] { "user.read" };
    PublicClientApplication app = new PublicClientApplication(clientId, authority);

        try
        {
            var securePassword = new SecureString();
            foreach (char c in "dummy")        // you should fetch the password
                securePassword.AppendChar(c);  // keystroke by keystroke

            result = await app.AcquireTokenByUsernamePasswordAsync(scopes, "[email protected]",
                                                                   securePassword);
        }
        catch(MsalException)
        {
          // See details below
        }

    Console.WriteLine(result.Account.Username);
}
like image 155
Alex AIT Avatar answered Oct 22 '25 19:10

Alex AIT


I actually find out a way to do it in "pure" C# without using the MSAL library, which I had some trouble with. So if you're looking for a solution w/o MSAL, you can do it the way described below.

Prerequisites

  • A user must exist in the AAD and must not use a Microsoft Account (source in Active Directory must not be "Microsoft Account").
  • A client application must be registered in the Azure Active Directory. The client app must be granted permissions to the app you want to test. If the client app is of type "Native", no client secret must be provided. If the client app is of type "Web app / api", a client secret must be provided. For testing purposes, its recommended to use an app of type "Native" without a client secret.

  • There must be no two factor authentication.

C# Code

You can than create a class "JwtFetcher" and use code like this:

    public JwtFetcher(string tenantId, string clientId, string resource)
    {
        this.tenantId = !string.IsNullOrEmpty(tenantId) ? tenantId : throw new ArgumentNullException(nameof(tenantId));
        this.clientId = !string.IsNullOrEmpty(clientId) ? clientId : throw new ArgumentNullException(nameof(clientId));
        this.resource = !string.IsNullOrEmpty(resource) ? resource : throw new ArgumentNullException(nameof(resource));
    }

    public async Task<string> GetAccessTokenAsync(string username, string password)
    {
        var requestContent = this.GetRequestContent(username, password);

        var client = new HttpClient
        {
            BaseAddress = new Uri(ApplicationConstant.Endpoint.BaseUrl)
        };

        var message = await client.PostAsync(this.tenantId + "/oauth2/token", requestContent).ConfigureAwait(false);

        message.EnsureSuccessStatusCode();

        var jsonResult = await message.Content.ReadAsStringAsync().ConfigureAwait(false);
        dynamic objectResult = JsonConvert.DeserializeObject(jsonResult);

        return objectResult.access_token.Value;
    }

    private FormUrlEncodedContent GetRequestContent(string username, string password)
    {
        List<KeyValuePair<string, string>> requestParameters = new List<KeyValuePair<string, string>>()
        {
            new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.GrantType, ApplicationConstant.RequestParameterValue.GrantTypePassword),
            new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Username, username),
            new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Password, password),
            new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.ClientId, this.clientId),
            new KeyValuePair<string, string>(ApplicationConstant.RequestParameterName.Resource, this.resource)
        };

        var httpContent = new FormUrlEncodedContent(requestParameters);
        return httpContent;
    }

The grant type for this is just "password".

like image 35
Tobias von Falkenhayn Avatar answered Oct 22 '25 19:10

Tobias von Falkenhayn



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!