AzureAD / azure-activedirectory-library-for-dotnet

ADAL authentication libraries for .net
http://aka.ms/aaddev
MIT License
357 stars 214 forks source link

UserPasswordCredential doesn't support .NET Core #482

Closed justinyoo closed 8 years ago

justinyoo commented 8 years ago

Hi, Team.

When I import this ADAL into my .NET Core library, it complains at the UserPasswordCredential class like:

image

It doesn't seem that I can create a custom class inheriting the UserCredential class for workaround. Is there any suggestion that I can use the UserPasswordCredential in .NET Core?

Cheers,

kpanwar commented 8 years ago

This is not supported by design.

justinyoo commented 8 years ago

@kpanwar Thanks for getting back to me.

Is there any timeline that we can use UserPasswordCredential in .NET Core? I don't understand "not supported by design". Does that mean UserPasswordCredential won't support .NET Core? If not, could you provide a workaround?

kpanwar commented 8 years ago

Yes, we will not add support for UserPasswordCredential in .NET core. There are other ways to authenticate as well. See https://github.com/Azure-Samples/active-directory-dotnet-deviceprofile

novogeek commented 8 years ago

We have a daemon service that was authenticating to an API as a user and we were using UserPasswordCredential method to fetch access token (by passing username and password). But when we upgraded the solution to .Net Core (and the latest ADAL package), this has broken. If UserPasswordCredential is no more supported, what is the alternate approach to authenticate non-interactively for the aforementioned scenario?

Squiggle commented 8 years ago

Ditto. I'm trying to authenticate against a 3rd party API in an Azure WebJob, which was previously working fine using UserPasswordCredential.

What is now the recommended approach here?

talynone commented 8 years ago

So how would one write a headless client for lets say CRM dynamics which requires an Oath 2.0 authentication to Active Directory to access the web api in .NET core?

psignoret commented 7 years ago

The idea here is that for apps to collect and store a user's username and password is not the correct approach for non-interactive authentication. Here are some details on why you shouldn't do this:

When you have a process running with no user interaction whatsoever, you should refer to the "Application Identity with OAuth 2.0 Client Credentials Grant" case of the "Daemon or Server Application to Web API" scenario. The following ADAL constructs will obtain an access token based on the app's identity only, and authenticate with either a client secret, a client assertion (i.e. a signed JWT token), or a client certificate (which will be used to create a signed JWT token). The resulting access token will not identify a user, and is only suitable when the token is for apps/APIs that have defined app-only permissions:

In scenarios where user interaction is possible, you can use the AcquireToken constructs that will pop up an authentication prompt (if applicable to your platform), or obtain the authorization code on your own and using AcquireTokenByAuthorizationCode. In both cases, a real live human user is expected to be present during the initial token acquisition, which will take place through a browser-based sign-in page.

If your device or platform is incapable of displaying Azure AD's browser-based sign in page (e.g. a device with limited input capabilities), you can use the device flow that @kpanwar has linked to.

After the first token acquisition, your app can use the cached refresh token to get a new access token without any further user actions or interactions (as long as nothing has changed that might invalidate the refresh token).

talynone commented 7 years ago

@psignoret My app works as an api proxy to the crm dynamics online web api (which is protected via AD oauth and application id). That's just the way web api in CRM works, I can't control its auth mechanism. The proxy is fully headless and has no concept or care of a logged in user. Other apps/services leverage this proxy api. There 100% no user interaction whatsoever in any way. Because of this I am forced to use the full .Net stack which supports this CRM web api headless login scenario. None of the links you provided have an acceptable alternative as they all rely on the concept of a logged in user. Obviously this was included in the full .net stack for a reason. This is the only feature holding back my entire solution from using .net core fully. It makes no sense to make the decision that .NET core never needs this and full stack may need it. If you're / Microsoft are so positive this scenario is never needed why is it in the full stack?

More details about this is documented here:

https://debajmecrm.com/2016/06/21/knowhow-authentication-with-dynamics-crm-online-web-api-without-user-login-screen-where-headless-authentication-works-and-where-not/

Ro3A commented 7 years ago

I'd like to present another similar use case just to highlight the obvious need for headless auth / user password credntials.

We have a customer portal where our users (construction contractors) can view their quotes (stored in CRM) as well other other business related documents like invoices (Dynamics AX) etc. This portal talks to our backend Web API app service that is only privately accessible. This API needs to call out to CRM's Web API.

Our users have no clue about AD, they don't have Microsoft accounts, and barely remember their logins for our site let alone any other. If another login screen came up just to access their quote information, they'd run and never come back.

We are a single tenant that wants to display quote information to our customers and we control all access to that data. We have created a non-interactive service account that "should" handle this.

I've tried every single workflow I've found. Client Credentials with generated Key, User Password, pure OAuth without ADAL library and using an older version of ADAL that still had UserPasswordCredential.

The latter got me very close to successfully accessing the Web API via a fixed service account but received an error claiming consent had not been granted and I found no way to grant this consent.

I got in touch with @dstrockis at https://github.com/Azure-Samples/active-directory-dotnet-native-headless/issues/12#issuecomment-260525462 who presented this solution to grant consent:

http://www.cloudidentity.com/blog/2016/10/04/provision-an-app-created-on-portal-azure-com-in-your-own-tenant/

All in all, like the others, I'm having to use the full framework and unfortunately the old OrganizationService to access data which I'm able to do very quickly and easily. I've not tried the solution mentioned above yet but it looks promising.

psignoret commented 7 years ago

@talynone:

Your scenario looks like a perfect candidate for the on-behalf-of flow (which is the only approach I didn't mention in my earlier comment). This flow is briefly described in the Daemon or Server Application to Web API scenario. It is an implementation of part of MS-OAPX (OAuth 2.0 Protocol Extensions), which is an extension to OAuth 2.0 (developed by Microsoft for AD FS).

To illustrate how this works, let's suppose you have the following:

  1. A client application that the end-user interacts with: ClientApp.
  2. A backend API used by your client app(s): BackendAPI (which has no user interface whatsoever, but is configured to use Azure AD for authentication).
  3. A third-party API that you have no control over, that requires user authentication via Azure AD: DynamicsAPI (which also has no user interface whatsoever).

With the on-behalf-of flow, the following would happen:

  1. The user signs in to ClientApp, by using any of the flows that include the user using the normal browser-based login flows. If the user needs to do MFA, provide consent, change their password, or any other sign-in interruption, they can do that. At the end of the sign-in, ClientApp will have an access tokenin the name of the authenticated user, destined for BackendAPI.
  2. ClientApp will present this access token to BackendAPI (e.g. in the Authorization HTTP header).
  3. BackendAPI will validate this token (e.g. check that its signature is good, that the claims are valid, etc.).
  4. Now, BackendAPI can't call DynamicsAPI and provide the same access token it was given (since it is destined for BackendAPI, and DynamicsAPI will reject it). Instead, BackendAPI needs to get a new access token, on behalf of the signed in user. To do this, BackendAPI makes an authenticated token request (meaning BackendAPI authenticates itself with a client secret or certificate) to Azure AD. This request includes:
    • An indication that the resulting access token should be for DynamicsAPI.
    • An indication that this is an "on-behalf-of" (OBO) request.
    • The original access token that BackendAPI received (remember, to get this token, ClientApp put the user through a sign-in, so it includes all the details about the user who signed in).
  5. If everything checks out, Azure AD will respond with an access token for DynamicsAPI, on behalf of the signed in user. BackendAPI can now call DynamicsAPI with that token.

Step 4, above, is very easily invoked with ADAL:

In the signature above, resource would be the ID of DynamicsAPI (e.g. "https://contoso.crm.dynamics.com"), clientCredential is how BackendAPI authenticates, and userAssertion is what contains the access token that ClientApp originally obtained for BackendAPI.

Here's a quick diagram illustrating the token and API requests happening:

image

You can see a working example of this in the active-directory-dotnet-webapi-onbehalfof sample. The on-behalf-of token request is executed in CallGraphAPIOnBehalfOfUser(). In that sample, one app (TodoListClient, a native client app) obtains an access token to a back-end API (TodoListService). TodoListService then uses that access token (which identified the authenticated user) to obtain an new access token to a different API (the Azure AD Graph API).

Ro3A commented 7 years ago

@talynone did not specify if his Web API was authenticated using AD or not. However, in my case, our end users are NOT registered in AD. We use AspNet Identity for our client, and the Web API is "internal only" protected via networking rather than user authentication.

If the Web API proxy is not AD integrated and the end users themselves are not AD users, I'm fairly certain this flow does not work. Is that accurate?

psignoret commented 7 years ago

@Ro3A hold on, I haven't addressed your scenario. I can't type that fast. :smile:.

psignoret commented 7 years ago

Edit (2017-04-08): This response mentions several times that Dynamics 365 (online) does not expose app-only permissions. This is only partly true, and it is possible to call Dynamics 365 with only a an application identity. See comments below for more details. I'm leaving the bulk of this response intact, because it is the case of some services/APIs.

@Ro3A:

Though I encourage you to read through my last response to @talynone, it's not going to help you, since the end-users accessing your app are not necessarily users that can sign in to your Azure AD tenant, let alone users that themselves have permissions to Dynamics CRM Online. (And @talynone, if this is also the case for you, then you should also read this.)

@dstrockis' link to @vibronet's blog post with the "consent invoker" tool will indeed work. (I just tried it myself.)

The root of your issue is that the resource that you are trying to access, Dynamics CRM Online, itself does not support the concept of app-only access. It always expects all requests will come from an authenticated user (directly or indirectly as with the on-behalf-of flow). You can see in the image below that the only permissions exposed are delegated permissions, which assume/require an authenticated user.

Dynamics CRM Online only exposes delegated permissions

So, let's walk through your options so far:

Edit (2017-04-08): You can use app-only authentication to Dynamics 365 (online). If you're here because of some other resource which doesn't have a work-around like Dynamics does, then continue to read...

Since we've exhausted all other options, then I agree that your only choice here is what you're trying to do. This option was removed from ADAL for .NET Core to strongly discourage transporting and persisting user credentials. However, the service (Azure AD) does support the Resource Owner Password Credentials Grant OAuth 2.0 flow, so you can skip using ADAL altogether for getting the token (though of course, you lose all the advantages of ADAL, particularly the token cache). You will need to make the following POST request (line breaks for display only):

POST https://login.microsoftonline.com/{tenant-id}/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=password
&resource={resource}
&username={username}
&password={password}
&client_id={client-id}

This request can be made with a simple HttpClient (make sure you do it right). The response will include the access token which you can use to access Dynamics CRM Online.

You would, of course, ensure that the user account that is being used as a "service account" has a complex password, that the password is stored securely (e.g. in Key Vault), that you are continuously rotating this password, and that the account has the least amount of permissions it needs (not just to CRM Online, but to everything else).

I can't say this enough: Only use this approach if you really, truly can't do what you need in any other way. "It's too hard", or "I'm in a hurry" are not a valid reasons for reducing the security of your systems and your data.

(Edited to remove reference invoking the Resource Owner Password Credential Grant flow as a confidential client, leaving it only as it would be invoked by a public client.)

kpanwar commented 7 years ago

@psignoret - resource owner grant for confidential clients was exposed as an error. We are going to disable that feature soon.

psignoret commented 7 years ago

Ah, thanks @kpanwar. Updated to show instead how a public client would invoke it.

Ro3A commented 7 years ago

So all that said, since what you're recommending is a last resort (and nearly a hack since the library doesn't support it), one must assume this use case is very small. From what I've seen across dozens of stack overflow articles and blogs, I believe it's a much larger use case and warrants a better solution.

In my case, it seems like AspNet Identity is a no go if the app needs access to AD protected resources. So in the long run, am I better off integrating our app with AD B2C and using the on-behalf-of flow? Even then, I'm not even sure B2C solution would work for us since we have user related tables in our Identity database. Can B2C store user related data?

gsacavdm commented 7 years ago

@Ro3A, B2C does support storing user related data. Here's a link that explains how you'd do this: https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-reference-custom-attr

Ro3A commented 7 years ago

I'll look into moving that direction then. Thanks for the guidance on this thread guys.

psignoret commented 7 years ago

@Ro3A: Careful though, you can't sign up for services such as CRM Online with an Azure AD B2C tenant. So though you can (and probably should) use Azure AD B2C as the identity service for your application, you won't be able to use the on-behalf-of flow to get access to the CRM Online API in the context of these users. (Because CRM Online won't recognize the user as a user who has permissions in CRM Online.)

Ro3A commented 7 years ago

That negates the reason to switch then because we'd be back at square one. I'm hoping you guys see the frustration at this point. This is definitely the most trouble I've ever had interacting with an API especially when the older version of the service is a breeze to connect to.

psignoret commented 7 years ago

@Ro3A: I do see your frustration. Like I said above, unfortunately this is a side-effect of the fact that CRM Online service itself does not currently support the concept of app-only API access, which is what your scenario calls for.

Each API or service that is secured by Azure AD declares which permissions it exposes, and whether these permissions are "app-only", or "app+user" permissions. The permissions will generally mirror the service's own authorization strategy. Since, CRM Online currently only exposes app+user permissions, this is a likely indication that the CRM Online service itself also only supports doing authorization based on a user identity.

The Resource Owner Password Credentials flow I mentioned above is a supported flow. Support for this was not included in ADAL v3 for .NET Core because it often leads to insecure practices (e.g. hard-coding username/password in a PowerShell script, etc.), but the flow itself is supported by the Azure AD service.

(I'll inquire with the Dynamic CRM team if there are any plans to support your scenario in a manner that does not require you to use a "service" user account.)

justin-cook commented 7 years ago

This post was written a couple days ago http://phuocle.net/crm/dynamics-365-online-s2s-authentication-full-explain.aspx and says he got it Dynamics CRM API working using client credentials flow.

psignoret commented 7 years ago

@justin-cook Thanks! Indeed, it looks like Dynamics 365 (online) now provides a mechanism to associate an "application user" (and that user's privileges within Dynamics 365) with an app ID. The client can then request an app-only access token to Dynamics 365, and Dynamics 365 will apply authorization based on it's own table (of application user to app ID), rather than OAuth 2.0 scopes and permissions.

This approach, while perfectly valid, is particular to Dynamics 365, so I'll update my comments above to clarify with the people of the future that there is a way to do this with Dynamics 365.

osca2000 commented 7 years ago

I like to have finger print authentication for my universal app. how can I do that without UserPasswordCredential? Most iPhone banking apps seem to have pwd and id saved but you are saying 'not recommended'. Can you add the feature to .net core and developers will decide if it is used? 'cached refresh token' may work with finger print authentication (not really authentication but lauching)?

hansmbakker commented 7 years ago

I want to make my dashboards and reports available in a web app or native app without requiring them to have a PowerBI account. According to the documentation you have to create a service account that will have access rights to the required elements.

In their examples they use UserPasswordCredential to get an Authentication Code.

Given that it is not possible by design to use a username/password credential to authenticate in the background, how can I authenticate an application with the PowerBI service then?

jmprieur commented 7 years ago

@wind-rider : for the moment, I suggest you use the OAuth2 device profile flow, as explained in this sample: this is not great, but could unblock you: https://github.com/Azure-Samples/active-directory-dotnet-deviceprofile ?

hansmbakker commented 7 years ago

@jmprieur thank you for your reply, but I'm not sure whether that helps. I'm wondering whether that login type can expire - I used it e.g. in VS Code to authenticate against Azure for some extensions and there you have to login every time you restart VS Code.

I want to give a select group of users viewing access to my PowerBI dashboard, without requiring them to have a PowerBI account. So I created a web app around it.

Does it mean that I should look in the server logs what the code (like B7D3SVXHV) is and use the old service account to login? Does that authentication expire? Because that is something I want to prevent - the users of my app should not be bothered with downtime because the device profile login expired...

jmprieur commented 7 years ago

Hi @wind-rider : For this particular use case, I believe you might want to use the Client credential flow then. An example is there: https://github.com/Azure-Samples/active-directory-dotnet-daemon-certificate-credential then I guess you could give access to this application to only the select group of user.

I would not look at server logs to infer tokens. That would really be a hack.

PhatHoang21 commented 7 years ago

AcquireTokenAsync(string resource, ClientCredential clientCredential) I used this method to obtain AD token. But I couldn't use that token to view my PowerBI report. The given error related to no permission:

Error: Microsoft.Rest.HttpOperationException: Operation returned an invalid status code 'Forbidden' Line: var reports = await client.Reports.GetReportsInGroupAsync(GroupId);

Any helps?

psignoret commented 7 years ago

@PhatHoang21 The signature you are using will obtain an app-only access token (i.e. an access token that only authenticates the client application, not any given user). To my knowledge, Power BI does not currently support the concept of app-only authorization to it's API, it only understands authenticated users.

sireza commented 7 years ago

I have a use-case that is 'slightly' different than what have been presented here so far.

Our app monitors several Office365 Outlook mailbox for emails. Access to these mailboxes are given to a service account created in Azure AD.

So we couldn't use the 'delegated' flow, as the app runs 'headless'. And we couldn't use the 'direct' app authentication flow either, because the app will need to be granted access to all mailboxes in our tenancy, which we don't want.

Other than manually acquiring the token ourselves as described above, is there any other method we could use since we are missing the UserPasswordCredential class?

mvperez commented 6 years ago

What is the alternative now for this to work? UserPasswordCredential is used in GetAccessToken() method. It's an aspnetcore (dotnetcore2.0) application which access service fabric cluster.

public FabricClient CreateSecuredFabricClient()
{
        try
        {
            var claimsCredentials = new ClaimsCredentials();
            claimsCredentials.ServerThumbprints.Add(_sslThumbprint);

            var fc = new FabricClient(claimsCredentials, _clusterConnectionString);

            fc.ClaimsRetrieval += (o, e) =>
            {
                var token = GetAccessToken(e.AzureActiveDirectoryMetadata);
                return token.Result.AccessToken;
            };

            return fc;
        }
        catch (FabricException fex)
        {
            Console.WriteLine("Connect failed: {0}", fex.InnerException.Message);
        }
        catch (Exception e)
        {
            Console.WriteLine("Connect failed: {0}", e.Message);
        }
        return null;
    }
private Task<AuthenticationResult> GetAccessToken(System.Fabric.Security.AzureActiveDirectoryMetadata aad)
    {
        var authContext = new AuthenticationContext(aad.Authority);

        var authResult = authContext.AcquireTokenAsync(
            aad.ClusterApplication,
            aad.ClientApplication,
            new UserPasswordCredential(_username, _password));
        return authResult;
    }
jmprieur commented 6 years ago

Is it an option for you to use the On-behalf-of grant as demonstrated in this sample? See https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore (the webapp and the web api are Asp.NET core)

This line shows how a controller aquires a token for a web api in the name of the user: https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore/blob/master/TodoListWebApp/Controllers/TodoController.cs#L36

note that this supposes to have cached the credentials as explained in https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore/blob/master/TodoListWebApp/Extensions/AzureAdAuthenticationBuilderExtensions.cs#L66-L74

mvperez commented 6 years ago

Hi @jmprieur , I can get a token from your examples (and others), but creating a FabricClient is slightly different. As you may noticed, getting access token requires service fabric ClusterApplication and ClientApplication which comes from System.Fabric.Security.AzureActiveDirectoryMetadata. If I just passed the token I get from ClientId+Secret it's invalid to FabricClient.

jmprieur commented 6 years ago

@marvinvperez : I'm not familiar with Fabric Client yet, let me try to understand this integrate with authentication.

mvperez commented 6 years ago

Thanks @jmprieur, removing UserPasswordCredential is a breaking changes to service fabric unless alternative is present. We need to do something like this. Although this UserCredential but here we can provide the password.

jmprieur commented 6 years ago

@marvinvperez : it was not removed. it's been in ADAL.NET for .NET Framework, but not in the other platforms (for instance .NET Core), and never was. What happened to Service Fabric, did they moved to .NET Core?

mvperez commented 6 years ago

From Service Fabric 6.1 (just released two weeks ago) now support .NET Core. But yes, if UserCredential/UserPasswordCredential don't support password in .NET Core, then accessing the secured service fabric cluster from .NET Core is impossible. We want to build our service fabric cluster maintenance in .NET Core for cross platform.

masnider commented 6 years ago

@jmprieur - do you think you could hop over to the SF version of this issue so we don't pollute here anymore and we can work with @marvinvperez to figure this out?

panmanphil commented 6 years ago

@jmprieur Looking at the on behalf of sample you posted, it would be great if you could do that in all cases, but one I'm hitting is how to test your code running in an integration test in jenkins. There is no user to force to log in interactively. I'm trying to test a shared component that uses graph api to work all the features Azure B2C has yet to implement, like changing an email address, and need to setup and delete users in the test. It seems that granting the service principal running the test, normally a web api, global admin access is the only option, but that feels kind of wrong too.

NiclasOlofsson commented 6 years ago

I too need this, since me and my users can't control their Office365 (EDU). For anyone struggling and want a full example of how to do the ADAL manually, there is a full example here https://github.com/NiclasOlofsson/MiNET/blob/MCEE-1.4/src/MiNET/MiNET/EduTokenManager.cs#L58

ankitbko commented 6 years ago

Atleast make this thing extensible, so other devs can plugin their own implementation of UserPasswordCredential flow and still get benefit of TokenCache and other features.

mixxit commented 5 years ago

We are currently suffering building headless background services that connect to WebAPI and make use of directory extensions as claims

Without UserPasswordCredentials we are forced to login via the Application authentication flow but microsoft azure ad graph does not support storing claims against a ServicePrincipal

After opening a ticket with microsoft they have recommended we instead authenticate as a user as directoryextensions are not support with the application flow

However we cannot login as a user stored in a appsettings.json as this is not supported by ADAL

This means we are required to design two systems where we accept claims from a user connecting to our APIs from their directory extensions and build a seconary claim inject process stored in a table for application authentication

markolb81 commented 5 years ago

Hi there, I still have the same problem like sireza: There is a headless proxy app (azure function) accessing the MS Graph to feed other services (with simple calendar entries) used by non-authenticated clients. App-only permissions are technically possible, but politically impossible and insecure, since my service then has complete access to users private calendars and more.

So, maybe I did not really understand what has been discussed above, but if using a "service user" with it's credentials is not the way to go here, what would you suggest as solution? In my point of view, there's a lack of more granular app-only permissions for the MS Graph, but that's probably another discussion.

Thanks for your hints!

jmprieur commented 5 years ago

Is it an option for you @markolb81 to move to MSAL.NET: it has U/P in .NET Core: See Username Password in MSAL.NET

markolb81 commented 5 years ago

Thanks for the idea @jmprieur , but MSAL.NET only supports User/Password for .NET Framework. Unfortunately I'm forced to use Azure Functions V2 which runs on .NET Standard 2.0.

ryanshane commented 5 years ago

Some options tested:

markolb81 commented 5 years ago

Hi ryanshane, thanks for the answer. Your option 3 is the way to go for me now - I already hat that Azure function anyway, so I acquire the token with a raw http-Request to https://login.microsoftonline.com/{TENANT_ID}/oauth2/token which still accepts the request parameters "resource={RESOURCE_URI}&client_id={CLIENT_ID}&grant_type=password&username={USERNAME}&password={PASSWORD}". This way, I do not use any library at all.

So, for the moment I am fine, but I understood that it's not very elegant. If there's any possibility to get rid of a service user (and therefore stored credentials), I would do it. Although I think I have a good overview about the possibilities, I am convinced that this is (for now) the one and only feasible solution for my use case. To explain, I will describe it here shortly if anyone is interested:

We developed a touchscreen device, allowing the user to book a meeting room via speech recognition on behalf of the user that is being recognized via face recognition. The touchscreen runs an UWP-App in "kiosk-mode" and is publicly usable by any employee. The user interface is restricted to simple touch input (even on-screen keyboard is not an option), face- and voice recognition. To actually access information, the touchscreen accesses an Azure Function which is used as a Proxy to the MS Graph and an SQL Database. Therefore one might understand, that even authentication by remote device (oauth device profile flow) is not feasible, because it will totally blow up the user experience.

The easiest technical solution is using app-only permissions (Calendars.ReadWrite), but since they are not granular and immediately give access to all private meetings, I consider that a no-go, even if I manually filter out sensitive information within the proxy azure function.

So, the only way to go is operating in a user-context, and since login by the actual user is not possible, a service user is the way to go. That, indeed, causes the next issue when trying to create a meeting on behalf of another user: It requires that every user in the organization which potentially uses that device creates a delegate permission on his calendar to that service user. Let's see if our IT dept. will accept a powershell solution here :)

Thanks however for your hints and help!

jmprieur commented 5 years ago

@markolb81 : Is it an option for you to migrate to MSAL.NET 2.x? it supports U/P in .NET Core?

ryanshane commented 5 years ago

Hey @markolb81 can you elaborate on that endpoint you're using? It only accepts POST? I tried putting the parameters in the posted data but receive 400 bad request.