-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
Target SharePoint environment
SharePoint Online
What SharePoint development model, framework, SDK or API is this about?
💥 SharePoint Framework
Developer environment
None
What browser(s) / client(s) have you tested
- 💥 Internet Explorer
- 💥 Microsoft Edge
- 💥 Google Chrome
- 💥 FireFox
- 💥 Safari
- mobile (iOS/iPadOS)
- mobile (Android)
- not applicable
- other (enter in the "Additional environment details" area below)
Additional environment details
- Android : 15.0
- Phone : Nothing (1)
- Teams Version : 1416/1.0.0.2026023402/0209
- First App tested
- SPFx version : 1.18.2
- Node.js version : 18.20.8
- @pnp/graph: 3.18.0
- @pnp/sp : 3.18.0
- Second App tested
- SPFx version : 1.21.1
- Node.js version : 22.15.0
- @pnp/graph: 4.13.0
- @pnp/sp: 4.13.0
Describe the bug / error
Description
Since the beginning of the year, Microsoft has enhanced the security for authentication of Android base Teams devices MC1088732. With this changes , the authentication/access token expired faster (approximately 1 hour) and is not refresh automatically in SPFx webpart in Teams Personal App on android Mobile.
Because of this, we are unable to call Graph's API with web API permission (User.ReadBasic.All, etc) in SPFx webpart
I have the same issue on all tenants i have access to (more than 4). In all of these tenants, MFA are applied on each tenant.(The API permissions User.ReadBasic.All is also authorized on all tenants)
I tried to log the error using my mobile phone and i got this error :
Error making HttpClient request in queryable [401] ::> {"error":{"code":"InvalidAuthenticationToken","message":"Lifetime validation failed, the token is expired."}}
Other colleges tried on their phones ( Google Pixel,Samsung with Android 16) and they have the same error by reproducing the same step (5 to 7) has describe below.
There is not links with network or organization settings.
I also tried on a second app in 1.21.1 with same error
I also tried with my webpart on a SharePoint Site by using the SharePoint Application on my android device and the Graph's call is working.
Workaround
If i sign-out and sing-in, the token if refresh and the Graph's API call works. But after approximately 1 hour, the access token expired, and the API call fails.
Questions
If it's the intended behavior of the Microsoft Changes MC1088732.
Do we simply need to update our Teams App version to 1449 and it's resolved ?
Or do we need to implement, in our apps, the refresh the access token ?
Thank you for your feedback !
Steps to reproduce
- Create a SPFx webpart
- Configure webApiPersmissionRequests in package-solution.json
"webApiPermissionRequests": [
{
"resource": "Microsoft Graph",
"scope": "User.ReadBasic.All"
}
]- Validate webApiPermission for User.ReadBasic.All in SharePoint Admin
- Make a Call using Graph API ( for ex: /me) by using choosing one if these options :
- PnP/Graph
import { Version } from "@microsoft/sp-core-library";
import { IPropertyPaneConfiguration } from "@microsoft/sp-property-pane";
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { ITheme, loadTheme } from "@fluentui/react";
import * as React from "react";
import * as ReactDom from "react-dom";
import Context from "../../services/Context";
import App from "./components/App";
import { SPFx as spSPFx } from "@pnp/sp";
import { SPFx as graphSPFx } from "@pnp/graph";
import { graph } from "@pnp/graph";
export default class ContosoAppWebPart extends BaseClientSideWebPart<never> {
public render(): void {
const element: React.ReactElement = React.createElement(App, {
context: this.context,
});
ReactDom.render(element, this.domElement);
}
protected onThemeChanged(theme: ITheme | undefined): void {
if (theme) loadTheme(theme);
}
protected onInit(): Promise<void> {
graph.using(graphSPFx(this.context));
await graph.me();
return super.onInit();
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse("1.0");
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [],
};
}
}- ServiceScope and the AadHttpClientFactory
- App
import { Version } from "@microsoft/sp-core-library";
import { IPropertyPaneConfiguration } from "@microsoft/sp-property-pane";
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";
import { initializeIcons, ITheme, loadTheme } from "@fluentui/react";
import * as React from "react";
import * as ReactDom from "react-dom";
import App from "./components/App";
import { IContosoApp} from "./components/ContosoAppProps";
import { GraphService } from "../../services/GraphService/GraphService";
export default class ContosoAppWebPart extends BaseClientSideWebPart<never> {
private _graphService: GraphService;
public render(): void {
const element: React.ReactElement<IContosoAppProps> = React.createElement(App, {
context: this.context,
clientWidth: this.context.domElement.clientWidth,
graphService: this._graphService
});
ReactDom.render(element, this.domElement);
}
protected onThemeChanged(theme: ITheme | undefined): void {
if (theme) loadTheme(theme);
}
protected async onInit(): Promise<void> {
if (this.context.sdks.microsoftTeams) initializeIcons();
this._graphService = this.context.serviceScope.consume(GraphService.serviceKey);
return super.onInit();
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse("1.0");
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: []
};
}
}```
- ServiceScope
```typescript
import { ServiceKey, ServiceScope } from "@microsoft/sp-core-library";
import { AadHttpClient, AadHttpClientFactory } from "@microsoft/sp-http";
import { IUser } from "../../models/User";
export class GraphService {
private static readonly graphBaseUrl: string = "https://graph.microsoft.com/v1.0";
public static readonly serviceKey: ServiceKey<IGraphService> = ServiceKey.create<IGraphService>(
"Custom:GraphService",
GraphService
);
private _aadHttpClientFactory: AadHttpClientFactory;
constructor(serviceScope: ServiceScope) {
serviceScope.whenFinished(() => {
this._aadHttpClientFactory = serviceScope.consume(AadHttpClientFactory.serviceKey);
});
}
/**
* Fetch the current user.
*/
public async getCurrentUser(): Promise<IUser> {
try {
const aadClient: AadHttpClient = await this._aadHttpClientFactory.getClient(
"https://graph.microsoft.com"
);
const response = await aadClient.get(
`https://graph.microsoft.com/v1.0/me`,
AadHttpClient.configurations.v1
);
if (!response.ok) {
throw new Error(`Error fetching current user: ${response.statusText}`);
}
const user: IUser = await response.json();
return user;
} catch (error) {
console.error("Error in getCurrentUser:", error);
throw error;
}
}
}
export const GraphServiceKey = ServiceKey.create<IGraphService>(
"my-custom.GraphService",
GraphService
);- Create a Teams Personal App using the SPFx Webpart
- Launch the App on a android mobile.
- Close your Teams and wait +-1 hour until the access token expired.
- Launch the App again
Expected behavior
The webpart will return a error if the access token expired :
Error making HttpClient request in queryable [401] ::> {"error":{"code":"InvalidAuthenticationToken","message":"Lifetime validation failed, the token is expired."}}```