SharePoint  

Send Email as Current User in SPFx with Microsoft Graph

This article explains how to send an email as the logged-in user from an SPFx web part using Microsoft Graph. It covers permission configuration (package-solution), admin consent, code examples (TypeScript + SPFx MSGraphClient), attachments, troubleshooting, and alternatives (application permissions). Ready-to-use code snippets are included.

Why use Microsoft Graph from SPFx?

  • Graph is the unified API for Microsoft 365 β€” it exposes me/sendMail to send mail as the currently authenticated user.

  • In SPFx you can call Graph with the built-in MSGraphClient (or AadHttpClient / GraphHttpClient), which handles auth as the current user so you don’t need to manage tokens client-side.

  • Typical use case: web part sends notification, confirmation, or formatted email using the identity of the user interacting with the page.

High-level steps

  1. Add the required delegated Graph permission (Mail.Send) to your SPFx solution.

  2. Package and request admin consent in SharePoint Admin (tenant admin must approve the permission).

  3. In your web part, get an MSGraphClient instance and call POST /me/sendMail with the message payload.

  4. Handle success and errors, and optionally include attachments and saveToSentItems.

Permission setup (package-solution.json)

Add a webApiPermissionRequests entry to config/package-solution.json so your solution requests delegated permission to send mail:

{
  "solution": {
    "name": "send-mail-solution",
    "id": "00000000-0000-0000-0000-000000000000",
    "version": "1.0.0.0",
    "webApiPermissionRequests": [
      {
        "resource": "Microsoft Graph",
        "scope": "Mail.Send"
      }
    ]
  }
}
  • After you deploy the package to the App Catalog, a tenant admin must go to the SharePoint admin center β†’ API access and grant the requested permission.

  • The permission requested above is delegated (mail is sent as the signed-in user). The tenant admin consent is required only once per tenant for that permission request.

Using MSGraphClient in SPFx (TypeScript example)

Below is a simple React + SPFx example that sends an email using /me/sendMail. This assumes you're in a web part and have access to this.context.msGraphClientFactory.

// imports
import * as React from 'react';
import { MSGraphClient } from '@microsoft/sp-http';
import { PrimaryButton, TextField } from '@fluentui/react';

// Example component props
interface ISendMailProps {
  context: any; // the web part context
}

export const SendMailComponent: React.FC<ISendMailProps> = ({ context }) => {
  const [to, setTo] = React.useState('');
  const [subject, setSubject] = React.useState('');
  const [body, setBody] = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const [status, setStatus] = React.useState<string | null>(null);

  const sendMail = async (): Promise<void> => {
    setLoading(true);
    setStatus(null);

    try {
      const client: MSGraphClient = await context.msGraphClientFactory.getClient();
      // Build message payload
      const message = {
        subject: subject || '(no subject)',
        body: {
          contentType: 'HTML',
          content: body || ''
        },
        toRecipients: to.split(';').map((addr: string) => ({ emailAddress: { address: addr.trim() } }))
      };

      // POST /me/sendMail
      await client.api('/me/sendMail').post({
        message,
        saveToSentItems: true
      });

      setStatus('Email sent successfully.');
    } catch (error) {
      console.error('sendMail error', error);
      // Example error handling
      if (error && error.status === 403) {
        setStatus('Permission denied β€” Mail.Send may not be consented for this tenant.');
      } else {
        setStatus(`Failed to send email: ${error?.message || JSON.stringify(error)}`);
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      <TextField label="To (separate multiple with ; )" value={to} onChange={(_, v) => setTo(v || '')} />
      <TextField label="Subject" value={subject} onChange={(_, v) => setSubject(v || '')} />
      <TextField label="Body (HTML allowed)" multiline value={body} onChange={(_, v) => setBody(v || '')} />
      <PrimaryButton text={loading ? 'Sending...' : 'Send email'} onClick={sendMail} disabled={loading} />
      {status && <div style={{ marginTop: 8 }}>{status}</div>}
    </div>
  );
};

Notes

  • Use context.msGraphClientFactory.getClient() to get an MSGraphClient already authenticated as the current user.

  • If you’re writing in a Web Part class (not a React separate component), this.context.msGraphClientFactory is available from the web part's context.

Message payload details

Example minimal Graph payload:

{
  "message": {
    "subject": "Hello from SPFx",
    "body": {
      "contentType": "HTML",
      "content": "<p>Hi there β€” this email was sent from an SPFx web part.</p>"
    },
    "toRecipients": [
      {
        "emailAddress": {
          "address": "[email protected]"
        }
      }
    ]
  },
  "saveToSentItems": true
}

Attachments

To include attachments, the message object supports attachments. For small attachments (<= 3 MB) you can embed fileAttachment objects with contentBytes (base64-encoded) and name. Example:

"attachments": [
  {
    "@odata.type": "#microsoft.graph.fileAttachment",
    "name": "hello.txt",
    "contentBytes": "aGVsbG8gd29ybGQ=",
    "contentType": "text/plain"
  }
]

For larger attachments, use the Outlook createUploadSession pattern through Graph (more complex, beyond this tutorial).

Sending as the user vs sending as another address

  • Send as current user: use POST /me/sendMail β€” requires Mail.Send delegated permission and the user in the current session. This is the approach we used above.

  • Send as another user: If you need to send as a different mailbox (/users/{id | userPrincipalName}/sendMail) you still need the appropriate delegated permission and the signed-in user must have permission to send on behalf of that mailbox (or use application permissions β€” more below).

  • Send as service (no user): use application permission Mail.Send on Microsoft Graph (app-only). This requires admin consent and tenant-wide permissions (very powerful). Then you call /users/{userId}/sendMail with a client credential flow (server-side). This is not supported directly in a client-side SPFx web part (because you cannot securely store client secrets in the browser).

Admin consent flow & deployment notes

  1. Add webApiPermissionRequests to package-solution.json as shown.

  2. Package solution: gulp bundle --ship and gulp package-solution --ship.

  3. Upload .sppkg to App Catalog and deploy.

  4. Tenant admin goes to SharePoint Admin β†’ API access (or Azure AD Enterprise Apps β†’ Consent) and Grant the requested Microsoft Graph permission (Mail.Send).

  5. After granted, your SPFx client can call the Graph endpoint as the signed-in user.

Common mistakes

  • Forgetting to deploy the updated package to the App Catalog (the permission request is added in the package).

  • Not waiting for admin to approve the requested permission.

  • Using an account without mailbox (e.g., some system accounts) β€” me/sendMail will fail if the account cannot send mail.

Troubleshooting

  • 403 / AccessDenied / Insufficient privileges

    • Check SharePoint Admin β†’ API access and ensure Mail.Send is granted.

    • Ensure the permission requested is delegated (Mail.Send), not application, if you intend to send as the current user.

  • 401 Unauthorized

    • Make sure you used msGraphClientFactory.getClient() and not a raw fetch without a token.

    • The mailbox couldn't be found or User does not have a mailbox

    • The signed-in user must have an Exchange mailbox.

  • CORS

    • Typically not an issue when using MSGraphClient because the library handles the authentication and headers.

  • Message not in Sent Items

    • Use saveToSentItems: true. If still missing, check mailbox settings (some tenants can override).

Security considerations

  • Do not store or hard-code access tokens or client secrets in client-side code.

  • Follow the least privilege principle: request only Mail.Send and not broader scopes.

  • If you require app-only access (application permission) to send on behalf of many users or service accounts, implement server-side code to hold client credentials and perform the Graph calls.

Advanced notes

Using GraphHttpClient vs MSGraphClient

  • MSGraphClient is a convenience wrapper around Graph and provides typed helpers. Preferred for Graph calls when available.

  • AadHttpClient/GraphHttpClient can be used when you need lower-level control. For me/sendMail, MSGraphClient is the simplest.

Sending templated HTML

  • Sanitize or validate any user-supplied HTML to reduce potential injection risks.

  • Use inline CSS sparingly; Outlook rendering can be inconsistent.

Logging and monitoring

  • Log errors to telemetry (Application Insights) from your web part so you can debug send failures in production.

  • If using app-only flows, consider using Azure AD sign-in logs and Graph usage logs for auditing.

Example: send mail with attachment (small file) β€” TypeScript snippet

const sendMailWithAttachment = async (client: MSGraphClient) => {
  const attachmentContent = btoa('Hello world from SPFx'); // base64
  const message = {
    subject: 'SPFx mail with attachment',
    body: { contentType: 'Text', content: 'Please find attachment.' },
    toRecipients: [{ emailAddress: { address: '[email protected]' }}],
    attachments: [{
      '@odata.type': '#microsoft.graph.fileAttachment',
      name: 'hello.txt',
      contentBytes: attachmentContent,
      contentType: 'text/plain'
    }]
  };

  await client.api('/me/sendMail').post({ message, saveToSentItems: true });
};

When to use server-side (application) approach instead

If you must send mail on behalf of users without them signing in (e.g., scheduled notifications from a backend), the server-side application permission route is appropriate. That requires:

  • Registering an Azure AD app with application permission Mail.Send.

  • Admin consent by tenant admin.

  • A secure backend that performs the OAuth2 client credentials flow and calls POST /users/{userId}/sendMail.

  • Do not put client secrets in SPFx (client-side code).

Recap / Checklist

  • Add Mail.Send to webApiPermissionRequests in package-solution.json.

  • Package & deploy solution to App Catalog.

  • Tenant admin grants consent in SharePoint Admin β†’ API access.

  • Use this.context.msGraphClientFactory.getClient() and call POST /me/sendMail.

  • Use saveToSentItems: true to save a copy in Sent Items.

  • Add error handling and telemetry.