Update to MSAL 2.7 and use of Microsoft.Graph SDK #19
Update to MSAL 2.7 and use of Microsoft.Graph SDK #19darrelmiller merged 22 commits intomicrosoftgraph:masterfrom
Conversation
- This checks deltas in mailFolders rather than drive - Updated Readme for clarifications - Removed Webclient dependencied to use the Graph API -
…untime - also abstracted away some hard coded values to config files.
|
@jmprieur could you review this from an MSAL dotnet perspective. This is the first one we're updating from old MSAL and addig that helper classes to allow us to update to next version easily next time. |
jmprieur
left a comment
There was a problem hiding this comment.
I've left a few suggestions.
@andrueastman, would you mind giving me access to your repo (I'm assuming this is a private repo, as I was not able to see it):
- I'd like to see the code in Visual Studio
- I could propose a PR on top of yours
Regards
Jean-Marc
|
|
||
| public MsalAuthenticationProvider(PublicClientApplication clientApplication, string[] scopes) { | ||
| _clientApplication = clientApplication; | ||
| _scopes = scopes; |
There was a problem hiding this comment.
It's weird that you pass the scopes in the MsalAuthenticationProvider? what will happen if your app wants to do a call to another Graph API (and therefore this requires more scopes) ?
There was a problem hiding this comment.
The idea would be to create a new GraphServiceClient and in effect MsalAuthenticationProvider objects with the right scopes and therefore make the calls to the other Graph API.
There was a problem hiding this comment.
@jmprieur In our actual AuthenticationProviders we allow scopes to be passed to the AuthenticationProvider via a "Request context" object. In .Net the request context is stored in the HttpRequestMessage.Properties dictionary and flows down the middleware pipe and is extracted by the AuthorizationProvider.
This will allow us to do incremental consent. There will be a fluent helper method on our GraphServiceClient to make it easy to pass scopes explicitly via the request and when we finally get permissions in the metadata we will embed these per request scopes into our code generated request builder.
| /// </summary> | ||
| public async Task<string> GetTokenAsync() | ||
| { | ||
| if (_accessToken == null) |
There was a problem hiding this comment.
Please never keep the access token in a variable
the right patter is to do:
try
{
authResult = _clientApplication.AcquireTokenSilentAsync(…)
}
catch(MsalUiRequiredException)
{
authResult = _clientApplication.AcquireTokenAsync()
}The reason is if you keep the token and there is a need for incremental consent or conditional access (for example multiple factor auth), your GetTokenAsync will always fail.
There was a problem hiding this comment.
This makes sense to me. Will action and resolve.
There was a problem hiding this comment.
@jmprieur Our goal here is not to try and support scenarios such as incremental consent and conditional access, but simply to provider a placeholder for the real AuthorizationProviders when they are ready. However, I agree that holding onto the token is not the best way of doing even this simple scenario.
Out of curiosity, is there an advantage to doing the try/catch over doing something like,
if (_account == null) {
authResult = _clientApplication.AquireTokenSilentAsync(_account);
} else {
authResult = _clientApplication.AquireTokenAsync();
_account = authResult.Account
}There was a problem hiding this comment.
@darrelmiller : there are many reasons why AcquireTokenSilentAsync would throw: that is if interaction is required from the user (password expired, conditional access needed, advanced security decides the user needs to re-sign-in etc …)
| 1. In Solution Explorer, select the **App.config** project. | ||
| 1. In Solution Explorer, select and edit the **App.config** in the ConsoleApplication project. | ||
|
|
||
| a. For the `AppPrincipalId` key, replace `To be filled in` with the application ID of your registered Azure application. |
There was a problem hiding this comment.
would that be the clientID?
Don't we want to be consistent with the authentication samples?
There was a problem hiding this comment.
Is the suggestion to use the word clientID rather than AppPrincipalId ??
There was a problem hiding this comment.
Yes :-) This sample seems really old and a little confused. Sorry, I didn't realize we were throwing such a convoluted sample at you as your first one.
There was a problem hiding this comment.
We might want to have a look at the https://github.com/Azure-Samples/active-directory-coding-exercises which was done for the London event in Oct. @dkershaw10 : do you agree?
| if (_accessToken == null) | ||
| { | ||
| AuthenticationResult authResult = null; | ||
| authResult = await _clientApplication.AcquireTokenAsync(_scopes); |
There was a problem hiding this comment.
Also this can throw. we need to handle the exception;
My proposal: let's work together on a class that you could reuse in all the samples.
There was a problem hiding this comment.
Also as you suggested, I'm happy to work together on this to make this class reusable.
There was a problem hiding this comment.
@jmprieur If we build an AuthorizationProvider class that works for all the samples, at what point are we reimplementing the classes we are doing in https://github.com/microsoftgraph/msgraph-sdk-dotnet-auth ? Perhaps we should focus on getting those completed faster, so the samples can take advantage of them?
|
thanks @andrueastman |
| // - retry mechanism to be implemented | ||
| // | ||
| operation(); | ||
| PublicClientApplication clientApplication = new PublicClientApplication(this.appPrincipalId, authority); |
There was a problem hiding this comment.
We should rename appPrincipalId to clientId. And is appPrinicpalPassword used? This sample seems to use Interactive flow, not ClientCredentials flow.
| /// <returns>Result from the Delta Query service.</returns> | ||
| public DeltaQueryResult DeltaQuery( | ||
| string entitySet, | ||
| public async Task<DeltaQueryResult> DeltaQueryAsync( |
There was a problem hiding this comment.
We can talk about this on Monday. The original sample is actually trying to be more than just a sample and it is trying to create a generic client that can query any arbitrary entityset for deltaquery results. That type of functionality belongs in our core SDK, not in a sample. So, I agree with what you are doing here by using graphServiceClient to demo doing a DeltaQuery on a single entityset. However, to reduce the confusion, I think we should lift this code out of the Client class into the program class.
There was a problem hiding this comment.
agreed, this sample is a little too confusing.
jthake
left a comment
There was a problem hiding this comment.
We will talk about this on Monday. But the readme instructions to get this working need updating and likely require some screen shots. So that people know what to do in portal.azure.com with Azure Active Directory to register the application.
- renamed AppPrincipalId to ClientId - fixed issue of storing the token in the AuthProvider to properly acquire it using the AcquireTokenSilentAsync function
- handled exceptions - updated config file to rename AppPrincipalId to clientID - changed Auth provider to handle local cache appropiately.
- Also made updated based on PR feedback.
| <add key="TenantDomainName" value="To be filled in"/> | ||
| <!--Service principal credentials--> | ||
| <add key="AppPrincipalId" value="edd4904c-ac06-4f13-b8b5-9ec799e82bd3"/> | ||
| <add key="AppPrincipalId" value="To be filled in"/> |
There was a problem hiding this comment.
Move this to a separate .config file that is not checked in to the repo (added to .gitignore). This is to prevent inadvertently leaking your app ID.
Look at https://github.com/microsoftgraph/msgraph-training-aspnetmvcapp/tree/master/Demos/03-add-msgraph/graph-tutorial. In that sample, a separate PrivateSettings.config is used to hold OAuth settings, and is merged into Web.config by using this line:
<appSettings file="PrivateSettings.config">
Then, a copy of PrivateSettings.config, named PrivateSettings.config.example is added to the repo with placeholders, so devs can just rename the file and plug in their own app ID/secret.
There was a problem hiding this comment.
Thanks for this @jasonjoh,
I have made updates to this where the Readme has been updated to instruct the user to first rename the appsettings.json.example file and the appsettings.json been added to the .gitignore to prevent leaking of ones appID.
| /// </summary> | ||
| public async Task<string> GetTokenAsync() | ||
| { | ||
| if (_accessToken == null) |
There was a problem hiding this comment.
@darrelmiller : there are many reasons why AcquireTokenSilentAsync would throw: that is if interaction is required from the user (password expired, conditional access needed, advanced security decides the user needs to re-sign-in etc …)
- used GraphServiceFactory - eliminated the client project as we are making graph calls directly now. - moved stuff around to factor in changes.
jmprieur
left a comment
There was a problem hiding this comment.
LGTM.
I've made a few suggestions for improvements.
You probably want to fix the README.md, and discuss which headers you want to have.
| @@ -0,0 +1,230 @@ | |||
| // <copyright file="DeltaQuery.cs" company="Microsoft"> | |||
There was a problem hiding this comment.
You probably want to have the same copyright header on all files?
probably the MIT one? @jthake-msft ?
|
Hey guys, I have made some updates to the PR. Please check them out when you can :). Thanks, @jthake-msft @jmprieur @jasonjoh @darrelmiller |
|
LGTM |
Essentially, this PR makes the following updates: -