
In 2024, the ArcGIS Maps SDK for .NET team introduced significant changes to authentication APIs as part of a long-term effort to simplify and align the .NET Maps SDK with other Native Maps SDKs. Key enhancements include:
- New credential types that align with common OAuth flows
- Server certificate validation callbacks for all platforms
- HTTP request/response interceptors for improved monitoring and debugging
- JSON-based credential persistence
- Deprecation of legacy APIs that accumulated complexity over time
This blog post will guide you through the transition, highlighting new capabilities, outlining clear paths from deprecated APIs, and providing practical code examples to help you implement these changes quickly and confidently.
New Credential Types
Let’s start by exploring the new credential types that form the foundation of the updated authentication system.
OAuthUserCredential (200.6+)
This replaces OAuthTokenCredentials of type OAuthAuthorizationCode.
OAuthUserCredential implements user authentication using OAuth “authorization code” flow with PKCE. This is the recommended approach for authenticating end users, providing extra security compared to other methods.
When a user signs in to your application with their ArcGIS account, a token is generated that authorizes your app to access services and content on their behalf. The available resources and functionality depend on the user’s ArcGIS account type, roles, and privileges.
To implement this authentication flow, you need to:
- 1) Create an OAuthUserConfiguration that specifies your OAuth client ID, redirect URL, and optional parameters.
- 2) Implement an IOAuthAuthorizeHandler to launch a web browser for user sign-in and receive the authorization code.
Our Access services with OAuth credentials tutorial provides a complete implementation example.
OAuthAppCredential (200.5+)
This replaces OAuthTokenCredentials of type OAuthClientCredentials.
OAuthApplicationCredential implements app authentication using OAuth “client credentials” flow. Use this to create applications that do not require users to sign in, but still need access to token-secured resources and services.
With this approach, requests are made using the credits and privileges associated with your app’s account rather than an individual user’s account. An app credential is created from a client ID and client secret that have been pre-registered with the portal.
The client secret should be treated as confidential information since it allows direct billing to your developer account. This authentication method is intended for secure environments where credentials cannot be easily exposed to end users. Never include your client secret in publicly distributed applications.
AccessTokenCredential (200.6+)
This replaces ArcGISTokenCredentials created by GenerateCredentialAsync.
AccessTokenCredential provides an access token for secured ArcGIS content and services. You can obtain this token in several ways:
- Username/Password: Call CreateAsync with a username and password. Note that this method is less secure than OAuth user authentication as it involves handling plaintext passwords.
- Network Security: Call CreateNetworkSecuredAsync for services protected by Integrated Windows Authentication (IWA) or client certificate authentication (PKI).
- Portal Federation: Tokens can be automatically generated for federated services after signing into the owning portal.
Our ArcGIS token challenge sample shows how to create this credential in response to an authentication challenge. You can also create and add a credential to the AuthenticationManager before accessing secured services.
PregeneratedTokenCredential (200.5+)
This replaces ArcGISTokenCredentials created by the token-string constructor.
PregeneratedTokenCredential accesses token-secured ArcGIS content and services using an independently generated token. This credential type gives you flexibility when integrating with custom authentication systems. Use it when you:
- Handle token generation outside of the Maps SDK
- Have an existing authentication system that generates ArcGIS tokens
- Need to use tokens provided by a separate backend service
New Supporting APIs
Beyond the credential types themselves, several supporting APIs have been added to enhance flexibility and control.
Server Certificate Validation (200.6+)
When working with HTTPS services, you can now customize how SSL/TLS certificates are validated. Specify a custom callback when configuring HTTP settings to examine all SSL connections made by the Maps SDK.
Server certificates are usually validated by the operating system using default policies and known Certificate Authorities, but your own validator can accept or reject connections based on custom criteria. For example:
- Accept server credentials that would otherwise be rejected by the system (useful for development servers, proxies, or self-signed certificates).
- Reject credentials that would otherwise be accepted by the system, enabling certificate pinning.
The callback uses the standard .NET RemoteCertificateValidationCallback signature. For more information including code examples, see Microsoft’s guide to custom X509Certificate validation.
IHttpMessageInterceptor (200.5+)
The new IHttpMessageInterceptor interface allows you to monitor, modify, or even mock HTTP requests and responses. This powerful capability can be configured globally when configuring HTTP settings on startup. Common use cases include:
Logging and Diagnostics: Log request and response details for monitoring and debugging:
public class LoggingHttpInterceptor : IHttpMessageInterceptor { public async Task SendAsync(HttpMessageInvoker invoker, HttpRequestMessage message, CancellationToken cancellationToken) { // Log the outgoing request details. Console.WriteLine($"Request: {message.Method} {message.RequestUri}"); // Execute the request, with a timer Stopwatch stopwatch = Stopwatch.StartNew(); HttpResponseMessage response = await invoker.SendAsync(message, cancellationToken); stopwatch.Stop(); // Log the response details along with the elapsed time. Console.WriteLine($"Response: {(int)response.StatusCode} {response.ReasonPhrase}, Elapsed Time: {stopwatch.ElapsedMilliseconds} ms"); return response; } }
Custom Headers: Modify headers in requests or responses to improve compatibility with specific services:
public class CustomHeaderInterceptor : IHttpMessageInterceptor { public Task SendAsync(HttpMessageInvoker invoker, HttpRequestMessage message, CancellationToken cancellationToken) { if (message.RequestUri?.Host == "mydomain.com") { // Add an extra request header for this domain only message.Headers.Add("X-CustomHeader", "myHeaderValue"); } return invoker.SendAsync(message, cancellationToken); } }
Mock Responses for Testing: Intercept requests and send mock responses instead of using live services:
public class DictionaryMockHttpInterceptor : IHttpMessageInterceptor { // Map known URLs to string responses. private readonly Dictionary<string, string> _mockResponses = new Dictionary<string, string> { { "http://example.com/api/test", "{\"result\":\"Test successful\"}" }, { "http://example.com/api/error", "{\"error\":\"Something went wrong\"}" } }; public Task SendAsync(HttpMessageInvoker invoker, HttpRequestMessage message, CancellationToken cancellationToken) { if (message.RequestUri != null && _mockResponses.TryGetValue(message.RequestUri.AbsoluteUri, out var responseContent)) { var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(responseContent, System.Text.Encoding.UTF8, "application/json") }; return Task.FromResult(response); } // If no mock response is found, proceed with the actual HTTP call. return invoker.SendAsync(message, cancellationToken); } }
Credential.ToJson/FromJson (200.5+)
Persisting credentials reduces the need for users to repeatedly authenticate, creating a smoother experience across application sessions. For most platforms we provide a ready-to-use implementation via CredentialPersistence.CreateDefaultAsync that securely stores credentials using platform-specific mechanisms. But for those who handle persistence themselves, we’ve added Credential.ToJson and Credential.FromJson(string) methods to make this easier. These JSON-based methods have several advantages over the previous ISerializable-based approach:
- Consistent API across all .NET platforms (MAUI, WPF, UWP)
- Better compatibility between SDK updates
- Simplified format that’s easier to debug and inspect
Migration Guidance
Now that you understand the new authentication components, let’s look at practical steps for migrating existing code.
Token Authentication
If your app uses TokenCredential to access token-secured ArcGIS resources, you should replace it with one of the ArcGISCredential implementations described above.
Instead of generating credentials using AuthenticationManager.GenerateCredentialAsync with GenerateTokenOptions, use one of these factory methods:
- For username and password authentication: AccessTokenCredential.CreateAsync
- For network-secured services (IWA, PKI): AccessTokenCredential.CreateNetworkSecuredAsync
- For app authentication: OAuthApplicationCredential.CreateAsync
- For user authentication: OAuthUserCredential.CreateAsync
If you were handling token generation yourself and constructing an ArcGISTokenCredential with a token string, construct a PregeneratedTokenCredential instead.
If your app relied on OAuth Implicit flow (TokenAuthenticationType.OAuthImplicit
), you should switch to a different flow. The “implicit” flow has been deprecated across the ArcGIS Platform, and support will be removed in the next major release of the Maps SDK.
Configuring OAuth
If your app registered a ServerInfo to configure authentication, migrate to the new APIs as follows:
For OAuth user authentication, create an OAuthUserConfiguration instead of ServerInfo:
// Old deprecated way: AuthenticationManager.Current.RegisterServer(new ServerInfo("http://myserver.com/portal/sharing/") { TokenAuthenticationType = TokenAuthenticationType.OAuthAuthorizationCode, OAuthClientInfo = new OAuthClientInfo("MyClientID", null), }); // New recommended way: var myOAuthConfig = new OAuthUserConfiguration("http://myserver.com/portal", "MyClientID", myRedirectUrl);
After creating the configuration, you have three options:
- 1) Create a credential immediately:
var credential = await OAuthUserCredential.CreateAsync(myOAuthConfig); AuthenticationManager.Current.AddCredential(credential);
- 2) Let the SDK generate the credential when needed (bypassing the ChallengeHandler) using the fluent configuration API:
ArcGISRuntimeEnvironment.Initialize(config => { config.ConfigureAuthentication(authConfig => { authConfig.AddOAuthUserConfiguration(myOAuthConfig); }); });
- 3) Let the SDK generate the credential when needed by adding to AuthenticationManager.OAuthUserConfigurations:
AuthenticationManager.Current.OAuthUserConfigurations.Add(myOAuthConfig); AuthenticationManager.Current.OAuthAuthorizeHandler = myAuthorizeHandler;
For OAuth app authentication, pass client ID and client secret directly to OAuthApplicationCredential.CreateAsync.
If you currently use ArcGISHttpClientHandler.DefaultReferer or GenerateTokenOptions.Referer to specify a referrer, use UseDefaultReferer in the fluent configuration API instead.
Credential URLs
Starting with version 200.7.0, Credential’s ServiceUri property will be deprecated in favor of ServerContext, which provides a more reliable URL for matching credentials with network requests.
ServiceUri preserves the original URL from the time of creation, often including unnecessary details from the ChallengeHandler like item IDs or query parameters. In contrast, ServerContext provides a normalized base URL that reflects how credentials are actually matched to requests:
- For CertificateCredential and ProxyCredential: normalized hostname and port
- For NetworkCredential: normalized hostname, port, and path (allowing multiple realms on one server)
- For ArcGISCredential: normalized hostname, port, and part of the path up to but not including
/rest
.
This change doesn’t affect how credentials are matched internally, but if your code specifically checks the ServiceUri property, consider using ServerContext instead.
Using Maps SDK’s Network Stack for Your Own Requests
To make requests using Maps SDK’s network stack, taking advantage of its authentication and caching features, use ArcGISHttpMessageHandler instead of the deprecated ArcGISHttpClientHandler. This provides the same functionality with better performance and improved challenge detection.
Request Logging and Interception
If you were using ArcGISHttpClientHandler’s global HttpRequestBegin and HttpRequestEnd events, replace them with the IHttpMessageInterceptor interface described earlier.
Persisting Credentials
The ISerializable support for Credentials is being deprecated in line with Microsoft’s effort to phase out legacy serialization in .NET.
We’ve already updated the default CredentialPersistence implementations to use FromJson/ToJson instead of DataContractSerializer. If your app implements custom credential storage, we recommend that you do the same.
Obtaining and Refreshing Tokens
If your app needs to know the token value or expiration, cast it to a specific subclass of ArcGISCredential and use either the async GetTokenInfoAsync method or the TokenInfo property. Calling GetTokenInfoAsync refreshes the token as needed, so the new credentials do not include a RefreshAsync() method.
Summary
Migrating to the new authentication system in the Native Maps SDK for .NET is essential to ensure your applications remain secure and up-to-date. By following the guidance provided in this post, you can take advantage of the new capabilities, streamline your authentication processes, and prepare for future updates. While deprecated APIs will continue working in 200.x releases of ArcGIS Maps SDK for .NET, they will be removed in the next major release (300.x).
To ensure a smooth transition, enable deprecation warnings in your build process to identify usage of outdated APIs early, and implement changes incrementally with thorough testing. Our documentation provides additional examples and tutorials to guide you through specific scenarios. If you encounter challenges during migration, reach out through Esri Community or through technical support.
Lastly, we’ve provided a quick reference guide below for authentication flow replacements. Happy migrating!
Quick Reference Guide
Authentication Flow Replacements
Use case | Legacy APIs | New Replacement |
---|---|---|
OAuth user authentication | OAuthTokenCredential |
OAuthUserCredential |
OAuth app authentication | OAuthTokenCredential |
OAuthAppCredential |
Username + password login | GenerateCredentialAsync |
AccessTokenCredential |
Manually generated tokens | ArcGISTokenCredential |
PregeneratedTokenCredential |
OAuth implicit flow | GenerateCredentialAsync + TokenAuthenticationType.OAuthImplicit |
Switch to authorization code flow |
Configuration Replacements
Use case | Legacy API | New Replacement |
---|---|---|
Set up user authentication | ServerInfo + OAuthClientInfo |
OAuthUserConfiguration |
Pick token type | TokenAuthenticationType |
Use a specific subclass of ArcGISCredential |
Specify a referrer | ArcGISHttpClientHandler.DefaultReferer or GenerateTokenOptions.Referer |
IArcGISHttpConfiguration.UseDefaultReferer |
Check credential’s URL | Credential.ServiceUri |
Credential.ServerContext (200.7+) |
Miscellaneous Replacements
Use case | Legacy API | New Replacement |
---|---|---|
Make HTTP requests | ArcGISHttpClientHandler |
ArcGISHttpMessageHandler |
Intercept HTTP requests | HttpRequestBegin/End events |
IHttpMessageInterceptor |
Persist credentials | ISerializable |
Credential.ToJson + Credential.FromJson |
Article Discussion: