POST 和范围的 MS Graph API(邮件)问题

MS Graph API (mail) issue with POST and scopes

试图让它工作并且 GET / Patch 工作得很好,但是 POST 给了我 HTTP STATUS 400 和 403。必须是有范围的东西。在 Azure AD 中,我设置了以下范围:

Mail.ReadWrite (Delegated)
Mail.ReadWrite (Application)
Mail.Send Delegated)
Mail.Send (Application)

因此,登录工作正常,也可以获取/修补消息。只有 POST 似乎不起作用。 请参阅代码以获取确切的错误消息。

角度 10

App.module

export function MSALInstanceFactory(): IPublicClientApplication {
      return new PublicClientApplication({
        auth: {
          clientId: 'xxxx',
          authority: 'https://login.microsoftonline.com/common/',
          redirectUri: '/',
          postLogoutRedirectUri: '/#/login'
        },
        cache: {
          cacheLocation: BrowserCacheLocation.LocalStorage,
          storeAuthStateInCookie: isIE, // set to true for IE 11
        },
        system: {
          loggerOptions: {
            loggerCallback,
            logLevel: LogLevel.Info,
            piiLoggingEnabled: false
          }
        }
      });
    }

    export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
      const protectedResourceMap = new Map<string, Array<string>>();
      protectedResourceMap.set('https://graph.microsoft.com/v1.0/me', ['user.read', 'mail.readWrite', 'email']);
      // also tried these scopes .. 
      // protectedResourceMap.set('https://graph.microsoft.com/v1.0', ['user.read', 'mail.readWrite', 'email']);
      // protectedResourceMap.set('https://graph.microsoft.com/v1.0/query', ['user.read', 'mail.readWrite', 'email']);
      // protectedResourceMap.set('https://graph.microsoft.com/v1.0/search/query', ['user.read', 'mail.readWrite', 'email']);

      return {
        interactionType: InteractionType.Redirect,
        protectedResourceMap
      };
    }

    export function MSALGuardConfigFactory(): MsalGuardConfiguration {
      return { interactionType: InteractionType.Redirect };
    }

@NgModule({
    imports: [
      BrowserModule,
      // etc..
    ],
    declarations: [AppComponent],
    providers: [
      NgEventBus,
      ChhServices,
      SynclogService,
      AppService,
      AuthService,
      GapiServices,
      {
        provide: ErrorHandler,
        useClass: ErrorService,
      },
      {
        provide: HTTP_INTERCEPTORS,
        useClass: MsalInterceptor,
        multi: true
      },
      {
        provide: MSAL_INSTANCE,
        useFactory: MSALInstanceFactory
      },
      {
        provide: MSAL_GUARD_CONFIG,
        useFactory: MSALGuardConfigFactory
      },
      {
        provide: MSAL_INTERCEPTOR_CONFIG,
        useFactory: MSALInterceptorConfigFactory
      },
      MsalService,
      MsalGuard,
      MsalBroadcastService
    ],
    bootstrap: [AppComponent],
  })
  export class AppModule { }

Auth.service

signIn() {
    console.log('AuthService::signIn');
    this.msalService.loginPopup().subscribe((result) => {
        this.accessToken = result['accessToken'];
        console.log('authority', result, this.accessToken);
    });
}

testGraphApi() {

    // 200 OK
    const apiGet = this.httpClient.get(`https://graph.microsoft.com/v1.0/me/messages/`).subscribe((data) => {
        console.log('get', '/me/messages', data);
    });

    const categories: any[] = ['custom'];
    const body = {
        subject: '2320, with tags',
        flag: { flagStatus: 'flagged' }, // notFlagged
        categories,
        body: {
            contentType: 'html',
            content: 'lalala'
        },
        inferenceClassification: 'other'
    };

    const id = 'AQMkADAwATM3ZmYAZS0zOTkANy02MTAwAC0wMAItMDAKAEYAAAM_TfJTK-tISYhjZdaCkkbgBwCPpkVcscQ9QJF-EDzB8h_oAAACAQwAAACPpkVcscQ9QJF-EDzB8h_oAAACHbIAAAA=';

    // 200 OK
    const apiPatch = this.httpClient.patch(`https://graph.microsoft.com/v1.0/me/messages/${id}`, body).subscribe((data) => {
        console.log('patch', '/me/messages', data);
    });

    const bodySendMail = {
        'message': {
            'subject': 'Meet for lunch?',
            'body': {
                'contentType': 'Text',
                'content': 'The new cafeteria is open.'
            },
            // etc..
        }
    }

    const headers = new HttpHeaders({ 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.accessToken}` });

    // 403 Forbidden
    // "code": "ErrorAccessDenied",
    // "message": "Access is denied. Check credentials and try again.",
    const apiSendMail = this.httpClient.post(`https://graph.microsoft.com/v1.0/me/sendMail`, bodySendMail, { headers }).subscribe((data) => {
        console.log('post', '/me/sendMail', data);
    });

    const bodySearch = {
        'requests': [
            {
                'entityTypes': [
                    'message'
                ],
                'query': {
                    'queryString': 'ref:6019d6bf1ce3425fb833559e'
                },
                'from': 0,
                'size': 5
            }
        ]
    }

    // 400 Bad Request
    // "code": "AuthenticationError",
    // "message": "Error authenticating with resource",
    const apiSearch = this.httpClient.post(`https://graph.microsoft.com/v1.0/search/query`, bodySearch, { headers }).subscribe((data) => {
        console.log('post', '/search/query', data);
    });
}
// 403 Forbidden
// "code": "ErrorAccessDenied",
// "message": "Access is denied. Check credentials and try again."

Send mail API 需要 Mail.Send 许可。当请求基于当前登录用户的 /me 端点时,它应该具有 delegated permission.

因此您需要在门户中添加 Mail.Send 委托权限并添加到您的代码中。

// 400 Bad Request
// "code": "AuthenticationError",
// "message": "Error authenticating with resource"

searchEntity: query API 需要 Mail.ReadWrite 委托权限。此 api 仅支持“工作或学校帐户”。工作帐户通常使用组织的自定义域名或公司名称,例如“jon@contoso.com”或“xxx@yourTenantName.onmicrosoft.com”.

您可以尝试在 Graph Explorer 中请求 api。