DropNet Integration - GetAccessTokenAsync error - c#

I'm trying to implement the DropNet library into my Windows Phone app but I'm getting the an error when calling the GetAccessTokenAsync method.
Step 1: Get the oauth token from Dropbox
public void ConnectToDropbox()
{
_client = new DropNetClient(API_KEY, API_SECRET);
_client.UseSandbox = true;
// Get Request Token (oauth token) from Dropbox
_client.GetTokenAsync(
(userLogin) =>
{
// Authorise app with Dropbox
var url = _client.BuildAuthorizeUrl();
browser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted);
browser.Navigate(new Uri(url));
},
(error) =>
{
Debug("Error: GetTokenAsync");
});
}
This seems to work correctly and returns an oauth authorisation code. The URL which the browser navigates to is
https://www.dropbox.com/1/oauth/authorize?oauth_token=TSLEY7lLS8K2Mmnr
Step 2: Convert the oauth token into usable Dropbox API token
void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
Debug("Callback URL: " + e.Uri.AbsoluteUri);
if (e.Uri.AbsolutePath == "/1/oauth/authorize")
{
//The User has logged in!
//Now to convert the Request Token into an Access Token
_client.GetAccessTokenAsync(
(response) =>
{
Debug("User is logged in");
LoadContents();
},
(error) =>
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
Debug("Error getting access token (GetAccessTokenAsync)");
MessageBox.Show(error.Message);
});
});
}
else
{
//Probably the login page loading, ignore
}
}
The GetAccessTokenAsync method throws the following exceptions at runtime:
An exception of type 'System.Net.WebException' occurred in System.Windows.ni.dll and wasn't handled before a managed/native boundary
The in-app MessageBox displays:
Exception of type 'DropNet.Exceptions.DropboxException' was thrown.
All the properties of the DropboxException object are as follows:
Response: RestSharp.RestResponse
Status Code: Unauthorized
Stack Trace:
Data: System.Collections.ListDictionaryInternal
Base Exception: DropNet.Exceptions.DropboxException: Exception of type 'DropNet.Exceptions.DropboxException' was thrown.
Inner Exception:
Type: DropNet.Exceptions.DropboxException
Is the the Status Code Unauthorized relevant? I'm using the appropriate API key and secret provdided by Dropbox.
I'd be grateful if anyone who's experienced similar issues when using DropNet could give me some advice in resolving this problem. Happy to provide any further info if needed.
Thanks in advance,
Andrew

Related

Smart home report state SYNC new device | Requested entity was not found. [404]

I've implemented the HomeGraph API with the help of the package Google.Apis.HomeGraphService.v1 (1.50.0.2260)
It seems to work fine as well, the ReportStateAndNotification function works fine on the query, execute, and some sync requests.
But when I add a new device to my system through our app and a SYNC request is sent to Google and comes in our backend, the HomeGraph API will return an exception when sending this sync request..
-> The sync request does not throw an exception when I modify a device name in our app. It only occurs when new devices are added.
I've searched through google and multiple StackOverflow posts.. But I'm probably missing something. Most posts say check the API key etc but then the ReportStateAndNotification function should always fail, not only when the sync request comes from Google to our backend.
Could anyone point me in the right direction?
Function that is used for sync requests:
public static void Send(Dictionary<string, object> deviceStateList, string requestId, string googleCustomerId)
{
string deviceIdList = String.Format("({0})", string.Join(", ", deviceStateList.Keys));
try
{
var jsonFilePath = _appSettingsRetriever.PrivateGoogleAuthenticationFile;
string scope = "https://www.googleapis.com/auth/homegraph";
using (var stream = new FileStream(jsonFilePath, FileMode.Open, FileAccess.Read))
{
GoogleCredential credentials = GoogleCredential.FromStream(stream);
if (credentials.IsCreateScopedRequired)
credentials = credentials.CreateScoped(scope);
HomeGraphServiceService service = new HomeGraphServiceService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials
});
var request = new ReportStateAndNotificationRequest
{
AgentUserId = googleCustomerId,
RequestId = requestId,
Payload = new StateAndNotificationPayload
{
Devices = new ReportStateAndNotificationDevice
{
States = deviceStateList
}
}
};
_log.Debug($"Sending to HomeGraph for devices: {deviceIdList} customer: {googleCustomerId} requestId: {requestId}");
DevicesResource.ReportStateAndNotificationRequest rp = service.Devices.ReportStateAndNotification(request);
ReportStateAndNotificationResponse resop = rp.Execute();
}
}
catch (Exception ex)
{
_log.Error($"Exception in ReportToHomeGraph for Customer: {googleCustomerId}. DeviceList: {deviceIdList}. JsonPath: {_appSettingsRetriever.PrivateGoogleAuthenticationFile} Exception: {ex}.");
}
}
Exception:
2021-09-24 14:16:13,547 [110] ERROR ReportToHomeGraph
Exception in ReportToHomeGraph for Customer: 05. DeviceList: (
fe965e6a-21ad-425f-b594-914bf63510a9,
1cc0ee97-a87f-44c5-a3e3-a39d159ee193,
618cdf94-2b31-434f-b91e-00837d155d4a
).
JsonPath: C:/myfile.json Exception: The service homegraph has thrown an exception:
Google.GoogleApiException: Google.Apis.Requests.RequestError
Requested entity was not found. [404]
Errors [
Message[Requested entity was not found.] Location[ - ] Reason[notFound] Domain[global]
]
at Google.Apis.Requests.ClientServiceRequest`1.<ParseResponse>d__35.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Google.Apis.Requests.ClientServiceRequest`1.Execute()
at BusinessLogic.GoogleAssistant.TokenService.HomeGraph.ReportToHomeGraph.Send(Dictionary`2 deviceStateList,
String requestId, String googleCustomerId) in C:\Repos\GoogleAssistant
.TokenService\HomeGraph\ReportToHomeGraph.cs:line 57.
When users add a new device, the first step you need to do is to issue a Request Sync to Google. This indicates the set of devices for that user has changed, and you need a new Sync request to update the data in homegraph. Google will follow this by delivering a Sync intent to your fulfillment endpoint, which you can respond with the updated set of devices.
Getting a 404 when calling Request Sync might indicate your Service Account Key might be invalid, or the agent user id you target might be wrong. Otherwise getting an error for your Sync Response might indicate it’s structured incorrectly. You can find out more about how to structure it in our examples.

Displaying Custom Server Side Error Message in Angular and .Net Core API

I have some rest API written in C# and the API is called from Angular (I am using version Angular 8). The call is fine and it is working fine. However, in case of any exception, I cannot display the customized error message in angular. For example, suppose I have a server side validation in C# which validates if the value of a field matches with the string "abc". If it does not match, it will throw an error and in UI (developed in Angular), I want to display the message
"Invalid String Specified".
My server side code is as below -
if (headerValues.Equals("abc")) {
throw new InvalidStringException("Invalid String specified", 999);
}
The Invalid InvalidStringException class is as below -
public class InvalidStringException : System.Exception
{
int status { get; set; }
public InvalidStringException() { }
public InvalidStringException(string message, int status) : base(message) {
this.status = status;
}
}
When that exception is thrown and caught in server side, it is available as 500 exception but could not print the custom message.
I am trying following code in Angular -
} catch (error) {
console.log("Error Status: ", error.status);
console.log("Error Status: ", error.message);
}
Please suggest how to handle that scenario.
The error object that your Angular app receives should be an instance of HttpErrorResponse
You could do something like this to handle http errors:
if (error instanceof HttpErrorResponse) {
if (!error.status) {
console.log(error.message || error.toString());
} else {
console.log(`error status : ${error.status} ${error.statusText}`);
switch (error.status) {
case 401:
this.router.navigateByUrl("/login");
break;
case 500:
this.router.navigateByUrl("/login");
console.log(`redirect to login`);
break;
}
}
} else {
console.error("Other Errors");
}
You are throwing an exception which is handled by C# exception handler and it will only return the custom error message specified in that handler.
To return a custom message, you need to return with http code like 4xx or 5xx.
new HttpResponseException(Request.CreateErrorResponse(System.Net.HttpStatusCode.Conflict, "Custom Message"));
Or you can return with 2xx and you have to parse this subscribe or then method e.g.
new System.Web.Http.Results.ResponseMessageResult(
Request.CreateResponse((HttpStatusCode)227, "Custom Error Message")
);
this.http.get().toPromise().then((response: any) => {
if (response.status == 227) {
return error;
} else {
return data;
}
return apiResponse;
}).catch(error => {
//nothing here
});
If throwing a exception is not really necessary, you can return status code 400 and a message using BadRequest:
if (headerValues.Equals("abc")) {
return BadRequest("Invalid String specified");
}
Are you explicitly catching the InvalidStringException in your .NET API controller and returning the custom message? If not, the response will be a generic HTTP 500 'Internal Server Error' response. I'd suggest explicitly catching the InvalidStringException in your .NET API controller and returning a 400 response with your custom message e.g.
try {
...
}
catch (InvalidStringException iex) {
return BadRequest(iex.message); // iex.message == Invalid String specified
}
When the InvalidStringException scenario occurs, This will return a HTTP 400 response with "Invalid String specified" as the response body. You should be able to log the error on Angular side as you're currently doing...
As other people have mentioned, you need to catch the exception and convert it to an appropriate HTTP response in your own code.
The reason for that is because if otherwise your exception is handled by ASP.NET Core using exception handling configuration you have, and it may vary:
With developer exception page
Usually in development, you will have code:
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
What it does is when your environment is Development, it turns on a special page for developers to see information of unhandled exceptions. It is only in this case, you get the exception stacktrace as well as the exception message in the response body.
Without developer exception page
Conversely, if the exception page is turned off (usually off for Production environment), you will see nothing in the response body.
How to fix
Given exception handling in ASP.NET Core is a cross-cutting concern, I wouldn't use try...catch around everywhere InvalidStringException needs to be converted to HttpResponse.
I would instead use either an IActionFilter or use UseExceptionHandler which is the exception handling middleware:
Here is an example of using UseExceptionHandler in Configure method in Startup.cs:
app.UseExceptionHandler(opt => opt.Run(
async ctx =>
{
var feature = ctx.Features.Get<IExceptionHandlerFeature>();
if (feature.Error is InvalidStringException ex)
{
await ctx.Response.WriteAsync(ex.Message);
}
}));
In this way, your InvalidStringException is handled globally in your application, without explicit try...catch. And you could throw the exception anywhere in your code, the above code would catch the exception and properly convert it to an HTTP response with your own message as the body.
Also note, because you are calling the API from an Angular app, so chances are you might need to set CORS up in your API application if the two applications run from different origins.
Without CORS, your HTTP request from the Angular app may fail before it can reach your API. In this case, the status of the HTTP response in your Angular app may be undefined. And in your console, you could see CORS errors.
you can use http interceptor to create general error handler for all http error in angular app,this way you can use alert ,redirect to login page in case token expired ,overwrite the error object and more but you can still access to the error object at the component level by add a callback for observable error.
Error Handler Service
#Injectable()
export class ErrorHandlerService implements HttpInterceptor {
constructor(private msgServ: MessageService) {}
public intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((err: HttpErrorResponse) => {
switch (err.status) {
case 500: {
this.msgServ.add({
severity: "error",
summary: "Error ",
detail: "Server is gone..πŸ’€"
});
break;
}
case 400: {
this.msgServ.add({
severity: "error",
summary: "Error ",
detail: "custome error message..."
});
break;
}
case 401: {
if (err.message == "invalid_token") {
// router πŸ‘‰ navigate to login
}
break;
}
default: {
this.msgServ.add({
severity: "error",
summary: "Error ",
detail: err.message
});
}
}
return throwError(err);
})
);
}
}
add the Interceptor to Providers in app module
#NgModule({
....
providers: [
{ provide: HTTP_INTERCEPTORS, useClass: ErrorHandlerService, multi: true },
MessageService
],
....
})
export class AppModule {}
demo πŸš€
MessageService is related to primeng component library ,you can use your own alert structure

How to fix "An unhandled exception occurred while processing the request."?

As we're developing a webapp which we want to authorize against a Shibboleth IDP we're getting the following error, after what seems like a successfull login at the IDPP:
An unhandled exception occurred while processing the request.
UnexpectedInResponseToException: Received message contains unexpected InResponseTo "idd95739d3bc9e44efa1154b3e62a2e121". No cookie preserving state from the request was found so the message was not expected to have an InResponseTo attribute. This error typically occurs if the cookie set when doing SP-initiated sign on have been lost.
Sustainsys.Saml2.Saml2P.Saml2Response.ReadAndValidateInResponseTo(XmlElement xml, Saml2Id expectedInResponseTo, IOptions options) in Saml2Response.cs, line 153
Our startup.cs looks like this:
services.AddAuthentication()
.AddSaml2(options =>
{
options.SPOptions.EntityId = new EntityId("https://adress.to.the.SP.net");
options.SPOptions.Compatibility.UnpackEntitiesDescriptorInIdentityProviderMetadata = true;
options.SPOptions.ReturnUrl = new Uri(#"https://adress.to.a.site.of.our.site.net.net/Saml/Session");
options.SPOptions.ServiceCertificates.Add(new X509Certificate2(#"wwwroot/mycert.pfx")); // "Sustainsys.Saml2.Tests.pfx"
options.IdentityProviders.Add(
new IdentityProvider(
new EntityId("adress.to.the.IDP.net"), options.SPOptions)
{
LoadMetadata = true,
MetadataLocation = ("https://adress.to.the.MetadataLocation.xml")
});
IdentityProvider idp;
var x = options.IdentityProviders.TryGetValue(new EntityId(Prov.Idp), out idp);
X509Certificate2 ssoTest = new X509Certificate2(#"wwwroot/sso-test.pem");
idp.SigningKeys.AddConfiguredKey(ssoTest);
});^^^
This is what I see when I check the browser's dev-tools cokkies option:
Gett 200 BIGipServer~idm~ipv4-shib-test: ! experimentation_subject_id: JSESSIONID: shib_idp_session:
Post 500 .AspNetCore.Antiforgery.w5W7x28NAIs: ARRAffinity: Saml2.t8NpWx0u6S6zBFc97nzgN_IL:
Gett 200 .AspNetCore.Antiforgery.w5W7x28NAIs: ARRAffinity: Saml2.5AYF3RXdiYbN3iolD0HCRu9P:

Why does call to api token during authentication fail in specific environment? (400 error)

Background/Problem: I am running an Angular app (with C#/.NET WebApi/DataAccess) and am getting the following errors when attempting to login (happens in DEV environment, not in REL):
Chrome Dev Tools Console errors:
POST http://localhost:1789/api/token 400 (Bad Request)
Backend
returned code 400, body was [object Object]
Error is only happening in DEV. Does not happen in REL or PROD. This was working in DEV previously.
What I've tried so far:
I am able to make calls to other classes and methods in the WebApi,
but not sure where this error came from.
I don't see any extra discernible information in "Network" tab of
Chrome Dev Tools (it just mentions the status code again)
I used the debugger in the Sources tab of Chrome dev tools and also
did not see any extra discernible information
There have not been any changes in the C# code that I can see from
source control. The Angular component and service are the same as
well.
I have tested other parts of the API as mentioned above
I have checked my config files multiple times and they seem to have
the appropriate values they used to have
I have looked up information about token authorization such as:
https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server
Spotify API bad request on api/token authorization Error: 400
(as well as all the other SO articles on 400 error for api token I
could find)
Debugged in Visual Studio: For OAuthAuthorizationServerOptions:
AccessTokenFormat is null. AuthorizeEndpointPath is {}. But
TokenEndpointPath is {/api/token}
Code:
login.component.ts:
cmdLogin_click()
{
if (this._authForm.value.username != null && this._authForm.value.password != "")
{
if (this._authForm.valid)
{
this.securityService.loginEndpoint(this._authForm.value.username,this._authForm.value.password)
.subscribe(response => {
this.setUser(response);
this.routerService.navigate(['/home']);
},
}
}
}
security.service.ts:
public loginEndpoint(email: string, password: string): Observable<any> {
let url = API_URL + '/api/token';
let ip = "";
const headers = new HttpHeaders().set('Content-Type', 'text/plain; charset=utf-8');
let content: string = "grant_type=password&username=" + email + "&password=" + password + "&ip=" + ip
return this.http.post(url, content, { headers: headers }).pipe(
catchError(this.handleError)
)
}
private handleError(error: HttpErrorResponse) {
if (error.error instanceof ErrorEvent) {
// A client-side or network error occurred. Handle it accordingly.
console.error('An error occurred:', error.error.message);
} else {
// The backend returned an unsuccessful response code. // The response body may contain clues as to what went wrong,
console.error(
`Backend returned code ${error.status}, ` +
`body was: ${error.error}`);
}
// return an observable with a user-facing error message
return throwError(
'Something bad happened; please try again later.');
};
Startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(25),
Provider = new EmdOAuthProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
}
My expected result is to be able to login without the error. My actual result is the errors listed above.
Been stuck on this for hours. Where should I be focusing at this point?

Code: TokenNotFound Message: User not found in token cache. Maybe the server was restarted

I have the following function to call users from active directory use graph api.
This function is hit on each keyup of a text box. But i am getting following error
Code: TokenNotFound Message: User not found in token cache. Maybe the
server was restarted.
at the line
var user = await graphClient.Users.Request().GetAsync();
Entire function Below:
public async Task<string> GetUsersJSONAsync(string textValue)
{
// email = email ?? User.Identity.Name ?? User.FindFirst("preferred_username").Value;
var identifier = User.FindFirst(Startup.ObjectIdentifierType)?.Value;
var graphClient = _graphSdkHelper.GetAuthenticatedClient(identifier);
string usersJSON = await GraphService.GetAllUserJson(graphClient, HttpContext, textValue);
return usersJSON;
}
public static async Task<string> GetAllUserJson(GraphServiceClient graphClient, HttpContext httpContext, string textValue)
{
// if (email == null) return JsonConvert.SerializeObject(new { Message = "Email address cannot be null." }, Formatting.Indented);
try
{
// Load user profile.
var user = await graphClient.Users.Request().GetAsync();
return JsonConvert.SerializeObject(user.Where(u => !string.IsNullOrEmpty(u.Surname) && ( u.Surname.ToLower().StartsWith(textValue) || u.Surname.ToUpper().StartsWith(textValue.ToUpper()))), Formatting.Indented);
}
catch (ServiceException e)
{
switch (e.Error.Code)
{
case "Request_ResourceNotFound":
case "ResourceNotFound":
case "ErrorItemNotFound":
//case "itemNotFound":
// return JsonConvert.SerializeObject(new { Message = $"User '{email}' was not found." }, Formatting.Indented);
//case "ErrorInvalidUser":
// return JsonConvert.SerializeObject(new { Message = $"The requested user '{email}' is invalid." }, Formatting.Indented);
case "AuthenticationFailure":
return JsonConvert.SerializeObject(new { e.Error.Message }, Formatting.Indented);
case "TokenNotFound":
await httpContext.ChallengeAsync();
return JsonConvert.SerializeObject(new { e.Error.Message }, Formatting.Indented);
default:
return JsonConvert.SerializeObject(new { Message = "An unknown error has occured." }, Formatting.Indented);
}
}
}
// Gets an access token. First tries to get the access token from the token cache.
// Using password (secret) to authenticate. Production apps should use a certificate.
public async Task<string> GetUserAccessTokenAsync(string userId)
{
_userTokenCache = new SessionTokenCache(userId, _memoryCache).GetCacheInstance();
var cca = new ConfidentialClientApplication(
_appId,
_redirectUri,
_credential,
_userTokenCache,
null);
if (!cca.Users.Any()) throw new ServiceException(new Error
{
Code = "TokenNotFound",
Message = "User not found in token cache. Maybe the server was restarted."
});
try
{
var result = await cca.AcquireTokenSilentAsync(_scopes, cca.Users.First());
return result.AccessToken;
}
// Unable to retrieve the access token silently.
catch (Exception)
{
throw new ServiceException(new Error
{
Code = GraphErrorCode.AuthenticationFailure.ToString(),
Message = "Caller needs to authenticate. Unable to retrieve the access token silently."
});
}
}
Can you help whats going wrong?
I know this is 4 months old - is this still an issue for you?
As the previous respondent pointed out, the error you're seeing is being thrown in the catch block in your code meant to handle an empty users collection.
In case you're stuck on this, or anyone else comes here - if you used this sample (or using ConfidentialClientApplication in any respect) and are throwing this exception, it's because your _userTokenCache has no users*. Of course, it's not because your AD has no users, otherwise you wouldn't be able to authenticate. Most likely, it is because a stale cookie in your browser is being passed as the access token to your authProvider. You can use Fiddler (or just check your localhost browser cookies) to find it (should be called AspNetCore.Cookies, but you may want to clear all of them).
If you're storing the tokencache in session (as the example is), remember that each time you start and stop the application, your working memory will be thrown out so the token provided by your browser will no longer match the new one your application will retrieve upon starting up again (unless, again, you've cleared the browser cookies).
*cca.Users is no longer used or supported by MSAL - you have to use cca.GetAccountsAsync(). If you have a deployed application running with the deprecated IUser implementation, you'll have to change this. Otherwise, in development your compiler will complain and not let you build, so you'll already know about this.
Looking at the code, it seems some chunks of logic are missing. For example, you got the method
public async Task<string> GetUserAccessTokenAsync(string userId)
but I can't see where this is being called. Besides that, I don't see the code for fetching a token from Azure AD either. Lastly, the error message you mention
Code: TokenNotFound Message: User not found in token cache. Maybe the server was restarted.
Seems like the error you're throwing
if (!cca.Users.Any()) throw new ServiceException(new Error
{
Code = "TokenNotFound",
Message = "User not found in token cache. Maybe the server was restarted."
});
Since the code isn't complete, I will try and make an assumption on what might be going wrong.
Firstly, assuming you're using MSAL.Net, a step in the acquisition of a token is missing.
The general flow is (Using GetTokenByAuthorizationCodeAsync())
Client challenges the user
User gets redirected and logs in
Callback is called and the client receives a code from the login process
Pass the code to GetTokenByAuthorizationCodeAsync() to obtain an id_token and depending on the permissions an access token.
GetTokenByAuthorizationCodeAsync() will store the token in the cache
that has been provided to the ConfidentialClientApplication
Retrieve the token from the cache with AcquireTokenSilentAsync()
If we fail to retrieve a token from the cache with AcquireTokenSilentAsync(), we'll request a new one from via
AcquireTokenAsync()
Most of this flow seems to be in place in your code, but it could be you're missing the actual token acquisition. Since no token is retrieved, no user is added to the ConfidentialClientApplication, which means cca.Users.Any() returns false, resulting in an ServiceError
Assuming the whole flow is in place, and you're actually acquiring a token, my second assumption would be that the _memoryCache are different. The _memoryCache in which you saved your token differs from the one you use to acquire a token silently.
I would recommend reading the documentation on token acquisition to determine the type of retrieving is the right fit for your application.
EDIT
Actually, I assume your code is inspired by this example.
What's especially interesting is this part
public GraphServiceClient GetAuthenticatedClient(string userId)
{
_graphClient = new GraphServiceClient(new DelegateAuthenticationProvider(
async requestMessage =>
{
// Passing tenant ID to the sample auth provider to use as a cache key
var accessToken = await _authProvider.GetUserAccessTokenAsync(userId);
...
}
return _graphClient;
}
What seems to be happening is that calling var user = await graphClient.Users.Request().GetAsync(); invokes the delegate that is provided to the GraphServiceClient. This in turn calls _authProvider.GetUserAccessTokenAsync(userId); which brings us to the public async Task<string> GetUserAccessTokenAsync(string userId) method. Our error most likely originates here, due to no Users being present in the ConfidentialClientApplication.Users collection
Hope this helps!

Categories