Graduate Program KB

User Authentication and Authorisation

  • Microsoft identity platform enables users to authenticate with Microsoft identities or social accounts
  • Components of Microsoft identity platform:
    • OAuth 2.0 and OpenID Connect: Authenticate with several identity types
    • Open-source libraries: Microsoft Authentication Libraries (MSAL) and support for other standards-compliant libraries
    • Microsoft identity platform endpoint: Integrates with the open-source libraries to implement human readable scopes compliant with industry standards
    • Application management portal: Registration and configuration experience in Azure portal
    • Application configuration API and PowerShell: Programmatic configuration of applications through Microsoft Graph API for automation

Service Principals

  • Delegation of Identity and Access Management functions to Entra ID requires an application to be registered with an Entra tenant
    • Create an identity configuration for your application to integrate with Entra ID
    • Single-tenant: Only accessible in your tenant
    • Multi-tenant: Accessible in other tenants
  • Entra applications are scoped to only one application object, residing in the tenant where the application was registered
    • Application objects are used as a template to create one or more service principal objects
    • Application objects describe how the service can issue tokens for application access, resources the application might need to access and the actions it can take
  • Entities requesting access to a resource secured by an Entra tenant must be represented by a security principal
    • Applies to both users (user principal) and applications (service principal)
  • Security principals define the access policy and permissions for the user/application in the Entra tenant, enabling features such as authentication and authorisation of resources
  • Three types of service principal:
    • Application:
      • A global application object in a single tenant, each created service principal references this object
      • The service principal object defines what the app can actually do in the specific tenant, who can access the app and what resources the app can access
    • Managed identity:
      • Represents managed identity, providing an identity for applications to use when connecting to resources supporting Entra authentication
      • Service principals representing managed identities can be granted access and permissions but can't be updated or modified directly
    • Legacy:
      • Represents a legacy app, which is an app created before app registrations were introduced
      • Legacy service principals can have credentials, service principal names, reply URLs and other properties that an authorised user can edit (no associated app registration)
  • Application objects vs. service principals:
    • The application object is the global representation of your application for use across all tenants and the service principal is the local representation for use in a specific tenant
    • The application object serves as the template from which common and default properties are derived for use in creating corresponding service principal objects
    • Application objects have a one-to-one relationship with the application and one-to-many relationships with its corresponding service principal objects
    • A service principal must be created in each tenant where the application is used to establish an identity for access to resources being secured by the tenant
    • Single-tenant apps have one service principal, multi-tenant apps has a service principal in each tenant where a user from that tenant consented to its use

Permissions and Content

  • Applications using the Microsoft identity platform follow an authorisation model, allowing users and admins to control data access
  • Microsoft identity platform uses OAuth 2.0 for third-party app access to web-hosted resources on behalf of users
  • Resources (ex. Microsoft Graph, Azure Key Vault) define permission sets to allow third-party apps to request only the permissions they need
  • Permission sets in OAuth 2.0 are known as scopes, represented as string values in the Microsoft identity platform (ex. https://graph.microsoft.com/Calendars.Read for calendar read access)
  • Apps typically request these permissions via the Microsoft identity authorize endpoint with specified scopes
    • Some high-privilege permissions require administrator consent and must be granted via the administrator consent endpoint
  • Microsoft identity platform supports two permission types:
    • Delegated access permissions: For apps with a signed-in user; permissions are granted by the user or an admin, allowing the app to act on behalf of the user
    • App-only access permissions: For apps without a signed-in user, such as background services; only an admin can grant these permissions
  • Consent is required for applications on the Microsoft identity platform to access resources or APIs, there are three consent types:
    • Static user consent:
      • All permissions are specified in the app's Azure portal configuration
      • User or admin is prompted for consent at first sign-in if permissions haven’t been granted
      • Admins can consent on behalf of the entire organisation
      • A drawback is it requires all permissions at initial sign-in, which can overwhelm users; limited flexibility for accessing unknown resources later
    • Incremental and dynamic user consent:
      • Apps request permissions incrementally based on user interactions, without needing all permissions upfront
      • New scopes can be requested during runtime, only requiring user consent for new permissions
      • Applies only to delegated permissions, not app-only access
      • High-privilege permissions requiring admin consent must still be pre-registered in Azure to allow admins to consent for the organisation
    • Admin consent:
      • Required for high-privilege permissions to provide extra control for organisational data access
      • Admin consent can be done on behalf of the entire organization if permissions are registered statically in the app
      • Ensures reduced admin cycles by predefining necessary permissions in the app registration portal
  • Requesting individual user consent:
    • In OpenID Connect or OAuth 2.0 requests, apps request necessary permissions using the scope query parameter
    • The scope parameter lists permissions (space-separated), each indicated by adding a permission value to the resource's identifier
    • Example:
      GET https://login.microsoftonline.com/common/oauth2/v2.0/authorize?
      client_id=00001111-aaaa-2222-bbbb-3333cccc4444
      &response_type=code
      &redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
      &response_mode=query
      &scope=
      https%3A%2F%2Fgraph.microsoft.com%2Fcalendars.read%20
      https%3A%2F%2Fgraph.microsoft.com%2Fmail.send
      &state=12345
      
    • Example request format includes parameters like client_id, response_type, redirect_uri, and scope with specific permissions
    • If user consent for the requested permissions doesn’t exist and admin consent hasn’t been provided, Microsoft identity platform prompts the user for consent

Microsoft Authentical Library (MSAL)

  • Enables developers to acquire tokens from the Microsoft Identity platform
  • Authenticate users and access secured web APIs
  • Benefits:
    • Don't need to directly use OAuth libraries or code against the protocol in your app
    • Acquires tokens on behalf of a user or application
    • Maintains token cache and refreshes tokens for you when they're close to expiry
    • Helps specify which audience can sign in
    • Helps set up application from configuration files
    • Helps troubleshoot app by exposing actional exceptions, logging and telemetry
  • MSAL can be acquired from many application types, including web apps, web APIs, single-page apps, mobile / native apps, daemons and server-side apps
  • Authentication flows:
    • Authorisation code: User sign-in and access to web APIs on behalf of the user
    • Client credentials: Access to web APIs by using the identity of the application itself
    • Device code: User sign-in and access to web APIs on behalf of the user on input-constrained devices like smart TVs and IoT devices
    • Implicit grant: User sign-in and access to web APIs on behalf of the user, not supported on desktop and mobile (use first option instead)
    • On-behalf-of (OBO): Access from the upstream web API to a downstream web API on behalf of the user
    • Username/password (ROPC): Allows an app to sign in the user by directly handling their password (not recommended)
    • Integrated Windows authentication (IWA): Allows apps on domain or Microsoft Entra joined computers to acquire a token silently (without UI interaction from the user)

Public Client and Confidential Client Applications

  • A client is a software entity that has a unique identifier assigned by an identity provider
  • There are two types of clients, public and confidential clients
    • They differ based on their ability to authenticate securely with the authorisation server
    • Public client applications run on devices (desktop, browserless APIs, mobile or client-side browser apps)
      • Can't be trusted to safely keep app secrets, they can only access web APIs on behalf of the user
      • Source or compiled bytecode of an app can be read, disassembled or inspected by untrusted parties
      • Can't have client secrets as they can't hold configuration time secrets
    • Confidential client applications run on servers (web apps, web API apps or service/daemon apps)
      • Considered difficult to access by users or attackers
      • Can hold configuration-time secrets to assert proof of identity
      • Client ID is exposed through the browser, but secret is only passed in the back channel and never directly exposed

Initialising Client Applications

  • With MSAL.NET 3.x, recommended way to instantiate an app is using the app builders PublicClientApplicationBuilder amd ConfidentialClientApplicationBuilder
    • Enable configuration of an app either from code, a configuration file or using both approaches
    // Instantiates a public client application
    IPublicClientApplication app = PublicClientApplicationBuilder.Create(clientId).Build();
    
    // Instantiate a confidential application, handling tokens from users in the Microsoft Azure public cloud
    string redirectUri = "https://myapp.azurewebsites.net";
    IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
        .WithClientSecret(clientSecret)
        .WithRedirectUri(redirectUri )
        .Build();
    
  • Need to register the app before initialising it so it can be integrated with the Microsoft identity platform, some relevant information includes:
    • Application (client) ID
    • Directory (tenant) ID: Provides identity and access management capabilities to apps and resources used by your organisation
    • Identity provider URL and the sign-in audience for your application (combined are known as the authority)
    • Client credentials: Can take the form of an application secret (client secret string) or certificate (of type X509Certificate2) if it's a confidential client app
    • For web apps, you need to set the Redirect URI where the identity provider will contact back your app with the security tokens
  • Using .with methods, we can apply modifiers on the application builders
    // .WithAuthority sets the app's default authority to a Microsoft Entra authority
    IPublicClientApplication app;
    app = PublicClientApplicationBuilder.Create(clientId)
        .WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
        .Build();
    
    // .WithRedirectUri overrides the default redirect URI
    IPublicClientApplication app;
    app = PublicClientApplicationBuilder.Create(client_id)
        .WithAuthority(AzureCloudInstance.AzurePublic, tenant_id)
        .WithRedirectUri("http://localhost")
        .Build();
    
  • Some common modifiers to set on a public or confidential client (some methods are exclusive to either such as .WithCertificate(X509Certificate2 certifcate) and .WithClientSecret(string clientSecret)):
    • .WithAuthority: Sets the application default authority to a Microsoft Entra authority, with the possibility of choosing the Azure Cloud, the audience, the tenant (tenant ID or domain name) or providing directly the authority URI
    • .WithTenant(string tenantId): Overrides the tenant ID or the tenant description
    • .WithClientId(string): Overrides the client ID
    • .WithRedirectUri(string redirectUri): Overrides default redirect URI
    • .WithDebugLoggingCallback(): If called, app calls Debug.Write to enable debugging traces
    • .WithLogging(): If called, app calls a callback with debugging traces
    • .WithTelemetry(TelemetryCallback telemetryCallback): Sets delegate used to send telemetry

Shared Access Signatures

  • A signed URI pointing to one or more storage resources and includes a token that contains a special set of query parameters
  • Signature is generated using SAS parameters and signed with a specific key for authorisation
  • Types of SAS:
    • User Delegation SAS:
      • Secured with Entra credentials
      • Exclusive to Blob storage
    • Service SAS:
      • Secured with the storage account key
      • Grants access to Blob, Queue, Table or Azure Files
    • Account SAS:
      • Secured with the storage account key
      • Grants access to multiple storage services and supports all operations of service/user delegation SAS
  • Components required to access data with SAS: Resource URI and SAS token
  • SAS token composition includes parameters defining permissions, start/end times, protocols, version and signature for authorisation
  • SAS token components:
    • sp: Access rights (ex. r for read, a for add, c for create, d for delete, l for list, w for write). Example: sp=r allows read-only access; sp=acdlrw grants all rights
    • st: Start time for access in UTC. Example: st=2020-01-20T11:42:32Z
    • se: Expiry time for access in UTC. Example: se=2020-01-20T19:42:32Z (8 hours)
    • sv: API version for Azure Storage. Example: sv=2019-02-02
    • sr: Resource type (b for blob, q for queue, t for table, etc.). Example: sr=b
    • sig: Cryptographic signature ensuring the token's authenticity
  • Best practices:
    • Use HTTPS
    • Prefer User Delegation SAS (removes need to storage account keys in code or use Entra ID for credential management)
    • Set minimal expiration time
    • Grant minimal required privileges
    • Avoid SAS where necessary (for high-risk scenarios, use middle-tier services instead of SAS)

Choose When to Use SAS

  • Use SAS when you need to:
    • Provide secure access to storage resources for clients without direct permissions
    • Provide read/write permissions to users for their own data in a storage account
  • Design patterns for SAS usage:
    • Front-End Proxy Service:
      • Clients interact with a proxy service for authentication and data validation
      • Suitable for enforcing business rules but may be expensive for large data or high transaction volumes
    • SAS Provider Service:
      • Lightweight service authenticates clients and generates SAS
      • Clients access storage resources directly using SAS, reducing load on the front-end proxy
  • The hybrid approach combines both previous design patterns:
    • Front-end proxy for data requiring processing/validation
    • SAS for direct access to less sensitive data
  • Copy operations:
    • Required for source authorisation when copying between different storage accounts or resource types
    • Examples:
      • Blob-to-blob or file-to-file in different accounts
      • Blob-to-file or file-to-blob, even within the same account

Stored Access Policies

  • Provides server-side control over service-level SAS
  • Groups SAS and enforces additional restrictions
  • Allows modification of start time, expiry time, permissions or revocation of SAS after issuance
  • Supported storage resources: Blob containers, file shares, queues and tables
  • Access policy for SAS consists of start time, expiry time and permissions
    • Parameters can be fully on the SAS URI, within the stored access policy or split between the two (but not duplicated)
  • Creating/Modifying a stored access policy:
    • Use Set ACL operation for the resource (ex. Set Container ACL, Set Queue ACL, Set Table ACL, Set Share ACL)
    • Request body requirements:
      • Signed Identifier: A unique name up to 64 characters
      • Optional Access Policy Parameters: Start time, expiry time and permissions
    BlobSignedIdentifier identifier = new BlobSignedIdentifier
    {
        Id = "stored access policy identifier",
        AccessPolicy = new BlobAccessPolicy
        {
            ExpiresOn = DateTimeOffset.UtcNow.AddHours(1),
            Permissions = "rw"
        }
    };
    
    blobContainer.SetAccessPolicy(permissions: new BlobSignedIdentifier[] { identifier });
    
    az storage container policy create \
        --name <stored access policy identifier> \
        --container-name <container name> \
        --start <start time UTC datetime> \
        --expiry <expiry time UTC datetime> \
        --permissions <(a)dd, (c)reate, (d)elete, (l)ist, (r)ead, or (w)rite> \
        --account-key <storage account key> \
        --account-name <storage account name> \
    
  • Modify a stored access policy:
    • Use the Set ACL operation to replace the existing policy
    • Ex. change permissions from read/write to read-only for future requests
  • Revoking a stored cccess policy:
    • Options:
      • Delete the policy: Removes access immediately
      • Rename the policy: Change the signed identifier to break associations with existing SAS
      • Set expiry time to the past: Invalidates associated SAS immediately
  • Removing access policies:
    • Single policy: Use Set ACL with the signed identifiers to retain
    • All policies: Use Set ACL with an empty request body to clear all policies
  • Immediate effects: Any deletion, renaming or modifications instantly affects all SAS linked to the policy

Microsoft Graph

  • Provides a unified programmability model for accessing and leveraging Microsoft 365 data
  • Main components:
    • Microsoft Graph API:
      • Single endpoint: https://graph.microsoft.com
      • Access via REST APIs or SDKs
      • Manages user and device identity, access, compliance, security and protects against data loss
    • Microsoft Graph Connectors:
      • Integrate external data (ex. Box, Google Drive, Jira, Salesforce) into Microsoft Graph services
      • Enhance experiences like Microsoft Search
    • Microsoft Graph Data Connect:
      • Tools for secure, scalable delivery of Microsoft Graph data to Azure data stores
      • Enables building intelligent applications with Azure development tools

Query Microsoft Graph by Using REST

  • Microsoft Graph API overview:
    • A RESTful web API for accessing Microsoft Cloud service resources
    • Requires app registration and authentication tokens (user/service-based)
    • Most resources, methods and enumerations are defined in the microsoft.graph namespace (ex. call records API in microsoft.graph.callRecords)
  • REST API request structure: Components:
    • HTTP Method: Specifies the operation (ex. GET, POST)
    • Version: API version in use (ex. v1.0, beta)
    • Resource: Target resource (ex. user, email)
    • Query Parameters: Optional OData query or REST method parameters for customisation
    {HTTP method} https://graph.microsoft.com/{version}/{resource}?{query-parameters}
    
  • REST API Response:
    • Status Code: HTTP code indicating success or failure
    • Response Message: Requested data or operation result (can be empty)
    • nextLink: URL for paginated data in @odata.nextLink for large result sets
  • HTTP Methods in Microsoft Graph:
    • GET: Reads data from a resource (no request body needed)
    • POST: Creates a new resource or performs an action (requires JSON request body)
    • PATCH: Updates a resource with new values (requires JSON request body)
    • PUT: Replaces a resource with a new one (requires JSON request body)
    • DELETE: Removes a resource (no request body needed)
  • API Versions:
    • v1.0:
      • Stable and generally available APIs
      • Recommended for production apps
    • beta:
      • Preview APIs that may change
      • Use only for testing apps in development, not for production
  • Resources:
    • Represents an entity or complex type, often defined with properties
    • Entities: Always include an id property
    • Examples of resources: me, user, group, drive, site
    • Relationships: Access related resources (ex. me/messages, me/drive)
    • Methods: Perform actions on resources (ex. me/sendMail to send an email)
    • Permissions:
      • Different resources and methods require specific permissions
      • Higher permissions are typically needed for creating or updating resources compared to reading them
  • Query Parameters:
    • Customise responses using OData system query options or other accepted parameters
    • Common uses:
      • Include/exclude specific properties in the response
      • Filter results based on specific conditions
    GET https://graph.microsoft.com/v1.0/me/messages?filter=emailAddress eq 'jon@contoso.com'
    

Query Microsoft Graph by Using SDKs

  • Simplify building efficient and resilient applications accessing Microsoft Graph
  • Two main components:
    • Service Library:
      • Generated from Microsoft Graph metadata
      • Provides models and request builders for a rich, discoverable experience
    • Core Library:
      • Enhances functionality across Microsoft Graph services with features like:
        • Retry handling
        • Secure redirects
        • Transparent authentication
        • Payload compression
      • Supports tasks such as:
        • Paging through collections
        • Creating batch requests
  • Microsoft Graph .NET SDK Packages:
    • Install the required NuGet packages based on your API version needs (v1.0 or beta)
    • Microsoft.Graph:
      • Accesses the v1.0 endpoint with a fluent API
      • Depends on Microsoft.Graph.Core
    • Microsoft.Graph.Beta:
      • Accesses the beta endpoint with a fluent API
      • Depends on Microsoft.Graph.Core
    • Microsoft.Graph.Core:
      • Core library for making calls to Microsoft Graph
  • Creating a Microsoft Graph client:
    • Choose an authentication provider and create the client instance
    var scopes = new[] { "User.Read" };
    
    // Multi-tenant apps can use "common",
    // single-tenant apps must use the tenant ID from the Azure portal
    var tenantId = "common";
    
    // Value from app registration
    var clientId = "YOUR_CLIENT_ID";
    
    // using Azure.Identity;
    var options = new TokenCredentialOptions
    {
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
    };
    
    // Callback function that receives the user prompt
    // Prompt contains the generated device code that you must
    // enter during the auth process in the browser
    Func<DeviceCodeInfo, CancellationToken, Task> callback = (code, cancellation) => {
        Console.WriteLine(code.Message);
        return Task.FromResult(0);
    };
    
    // /dotnet/api/azure.identity.devicecodecredential
    var deviceCodeCredential = new DeviceCodeCredential(
        callback, tenantId, clientId, options);
    
    var graphClient = new GraphServiceClient(deviceCodeCredential, scopes);
    
  • To read information from Microsoft Graph, create a request object and run the GET method on the request
    // GET https://graph.microsoft.com/v1.0/me
    
    var user = await graphClient.Me
        .GetAsync();
    
  • Retrieving a list of entities:
    • Similar to retrieving a single entity but with additional request configuration options
    • $filter: This query parameter reduces result set to rows matching a specific condition
    • $orderBy: This query parameter sorts list of entities by specified properties
    // GET https://graph.microsoft.com/v1.0/me/messages?$select=subject,sender&$filter=<some condition>&orderBy=receivedDateTime
    
    var messages = await graphClient.Me.Messages
        .Request()
        .Select(m => new {
            m.Subject,
            m.Sender
        })
        .Filter("<filter condition>")
        .OrderBy("receivedDateTime")
        .GetAsync();
    
  • Delete an entity involves constructing a request similar to retrieving an entity but use the DELETE request instead of GET
    // DELETE https://graph.microsoft.com/v1.0/me/messages/{message-id}
    
    string messageId = "AQMkAGUy...";
    var message = await graphClient.Me.Messages[messageId]
        .Request()
        .DeleteAsync();
    
  • Create a new entity:
    • New SDKs supporting a fluent style can use the Add method to add new items to collections
    • For template-based SDKs, the request object exposes a post method
    // POST https://graph.microsoft.com/v1.0/me/calendars
    
    var calendar = new Calendar
    {
        Name = "Volunteer"
    };
    
    var newCalendar = await graphClient.Me.Calendars
        .Request()
        .AddAsync(calendar);
    

Apply Best Practices to Microsoft Graph

  • Authentication:
    • Use OAuth 2.0 access tokens via:
      • HTTP Authorization header as a Bearer token
      • Graph client constructor with a Microsoft Graph client library
    • Use Microsoft Authentication Library (MSAL) to acquire tokens
  • Consent and authorisation:
    • Least privilege
    • Permission types:
      • Delegated permissions for interactive apps with signed-in users
      • Application permissions for background services without users (avoid for interactive scenarios)
    • End-user/admin experience:
      • Match permission requests to who consents (end-user vs. admin)
      • Understand static, dynamic and incremental consent flows
    • Multi-Tenant Applications:
      • Handle varying tenant settings (ex. admin-only consent or custom policies)
      • Expect and manage 403 errors for restricted actions
  • Handling responses effectively:
    • Pagination:
      • Resource collections may return results in multiple pages due to server-side limits
      • Use the odata.nextLink property to fetch subsequent pages until no @odata.nextLink is present
    • Evolvable enumerations:
      • Microsoft Graph can add new members to enums without breaking existing applications
      • By default, GET requests return only known members
      • To handle unknown members, use the Prefer HTTP request header to opt in for receiving them
  • Storing data locally:
    • Prefer making real-time calls to Microsoft Graph when possible
    • Cache/store data locally only for specific justified scenarios, ensure compliance with terms of use, privacy policy and Microsoft APIs Term of Use
    • Implement appropriate data retention and deletion mechanisms to manage stored data responsibly