A CTO choosing between in-house .NET developers and consultants can make the wrong decision before interviewing anyone: buying an identity provider whose SDK becomes inseparable from the application. My position is to buy identity operations only after a small team proves it can replace the provider. That test tells you more about the staffing you need than a vendor demonstration or a consultant’s implementation estimate.
A replacement drill reveals what a vendor demonstration hides
Ask each prospective identity vendor to support a working replacement drill in an ASP.NET Core application. The drill starts with a user signing in through the proposed provider and ends with the same application accepting a second OpenID Connect provider, using its own issuer and keys, without rewriting business authorization. A demonstration of the vendor’s sample application does not count, because samples rarely contain your claims, account links, administrative roles, or migration constraints.
Set aside 16 engineer-hours as a planning allowance for the first drill, with one application engineer and one person who understands identity operations. That is a proposed budget, not an industry benchmark. If the team cannot complete it, record precisely what blocked the switch: application code, provider configuration, missing export rights, or an assumption about user identity. An overrun caused by unfamiliar configuration calls for different help than one caused by vendor-only tokens embedded throughout the codebase.
Use a deliberately narrow application: one sign-in route, one protected API endpoint, and one authorization decision based on an application-owned role. Require the team to change the identity provider without changing that role’s meaning. The provider may authenticate a person; it should not be the sole authority on whether that person can approve an invoice or administer an account, because those decisions may need to survive a provider change.
In-House .NET Developers vs .NET Consultants: Key Decision Guide frames ownership as a staffing decision; I would make the replacement drill the first ownership decision because it exposes which knowledge and code must remain inside the company. A consultant can run the drill, but an employee should review its results and be able to repeat it. Otherwise, the company has bought an exit plan that leaves with the consultant.
I would not choose a provider because its .NET SDK makes the first login fast, because that advantage can disappear when SDK types spread into controllers, database records, and authorization policies. Count those dependencies during the drill. A thin adapter is useful only if the rest of the application can run against a second implementation; a folder named “abstractions” proves nothing by itself.
Standards matter only when your application passes the same tests twice
Require OpenID Connect discovery, OAuth 2.0 authorization code flow with PKCE as specified in RFC 7636, and documented handling of JWT claims under RFC 7519. These names are procurement requirements rather than proof of portability: providers can follow the protocols while issuing different claim names, subject identifiers, and group representations. Test the application against two actual configurations instead of accepting a standards checklist.
Start with a discovery probe. With Python 3 installed, set OIDC_ISSUER to a trusted test issuer URL and run the following file. It checks that discovery and the published JSON Web Key Set are reachable; it does not claim to validate a login or a token.
import json, os, urllib.request
issuer = os.environ["OIDC_ISSUER"].rstrip("/")
with urllib.request.urlopen(issuer + "/.well-known/openid-configuration", timeout=5) as response:
doc = json.load(response)
assert doc["issuer"].rstrip("/") == issuer, "issuer mismatch"
for key in ("authorization_endpoint", "token_endpoint", "jwks_uri"):
assert doc[key].startswith("https://"), f"invalid {key}"
with urllib.request.urlopen(doc["jwks_uri"], timeout=5) as response:
jwks = json.load(response)
assert jwks.get("keys"), "no published signing keys"
print("Discovery and JWKS reachable; token validation remains untested")
The 5-second timeout is a value to tune for the test environment, not a production resilience policy. Run the probe only against issuers you have approved, because a URL supplied by an untrusted party would turn this check into an outbound-request risk. If discovery fails, ask the vendor for the exact issuer URL before writing a provider-specific workaround; the failure may be configuration rather than incompatibility.
The harder test belongs in ASP.NET Core. Configure Microsoft.AspNetCore.Authentication.OpenIdConnect for interactive sign-in and Microsoft.AspNetCore.Authentication.JwtBearer for bearer-token APIs, then repeat the same tests for each provider. Verify issuer, audience, signature, expiry, and allowed signing algorithms rather than treating a parsable JWT as a valid one. Exercise a signing-key rotation through the provider’s JWKS endpoint, because a configuration that works only with today’s key will fail during an ordinary operational change.
Make the acceptance threshold three passing scenarios per provider: a valid sign-in, a rejected token with the wrong audience, and a user whose provider-side group changes while the application-owned role does not. Three is a deliberately small gate for procurement, not a complete security test suite. Record the actual elapsed swap time and the files changed in each run. Those are measurements from your application; a vendor’s assertion that migration is “straightforward” is not.
Check account continuity separately. An OpenID Connect sub claim identifies a user within an issuer’s context; it is not a portable customer ID, because a replacement provider can assign a different subject. Keep an application-owned account identifier and test how an authenticated person is linked to it after the switch. Email address alone is a poor automatic linking key, because addresses can change or be reassigned. If the vendor proposes SCIM 2.0 provisioning under RFC 7644, test deactivation and reactivation as well as creation; a successful import does not show what happens when access must be removed.
Buying identity operations beats building them, but only behind an application boundary
The explicit choice is Auth0 as a managed identity provider versus ASP.NET Core Identity with an application-operated user store. Auth0 wins when the team needs a provider to operate login infrastructure and can meet its identity requirements through documented protocols; its costs include subscription charges, configuration ownership, and migration work if its features become application dependencies. ASP.NET Core Identity wins when the company needs direct control over user records and can operate the supporting systems responsibly; its costs include engineering time, security review, recovery procedures, and ongoing incident ownership. Neither option makes application authorization someone else’s responsibility.
Do not compare the two using the number of screens a consultant can build in a week. Ask instead which work the purchase removes and which work it merely relocates. A managed provider can operate the credential system while your team still owns account linking, role definitions, token-validation settings, audit requirements, and the consequences of an outage. A self-operated implementation avoids a provider contract but does not avoid those duties either.
Request a written price for your expected active users, required connections, support level, and export assistance. A vendor-published 99.9% availability figure, if offered for the contracted service, is an input to review rather than a prediction of your application’s availability, because your redirect handling and token-validation path can fail independently. Likewise, request the rate limits that apply to management and provisioning APIs, since a migration script can work in a test tenant and stall during a full export.
Assign implementation accordingly. A consultant is a sensible purchase for a time-bounded protocol review or the first replacement drill if the current team lacks that experience, because the deliverable can be inspected and rerun. An in-house developer should own the adapter, account mapping, and regression tests after handover, because those components change alongside the application. If no employee can explain why a token was accepted or rejected, the proposed staffing arrangement has left a critical production decision outside the team.
An exit clause is useful only when the team can exercise it
Before signing, ask the vendor to supply a sample export with documented identifiers, timestamps, status fields, and any fields that cannot be exported. Then have the application team map that sample into its own account model. This is more revealing than an assurance that “data belongs to the customer,” because ownership language does not specify whether the data arrives in a form that preserves account continuity.
Put concrete assistance in the agreement: the export format, how to request an export, access to a test tenant, and how long support remains available after notice of termination. Ask who will help reconcile disabled accounts and unresolved identities. Do not demand a guarantee that passwords will migrate between providers; credential material may be deliberately unavailable for export, so the realistic plan may require users to reauthenticate or reset credentials. Price and test that plan before treating migration as solved.
Hire NET Developers or NET Consultant Key Decision Guide makes the hiring choice explicit; I would postpone that choice until the exit artifacts identify the work, because “hire a developer” and “hire a consultant” are inadequate specifications for account reconciliation or token review. Give bidders the same drill results and ask each to price a bounded deliverable. Compare the proposed tests, handover materials, and access required, not just the day rate.
Finally, decide what evidence would cause you to decline the vendor. Examples include an export that omits identifiers needed for reconciliation, an SDK dependency that the team cannot isolate, or a second-provider test that requires rewriting business roles. Write those rejection conditions before the sales call, because changing them after seeing a polished demonstration makes the evaluation difficult to audit.
Run the switch before choosing who owns it
Start by selecting one protected endpoint and one application-owned role, then ask your team to make them work with two test identity providers. Save the test results, changed files, elapsed time, and unresolved account-linking questions. Take that evidence to vendors and staffing candidates. The first purchase should be the smallest piece of help needed to complete and repeat the switch—not a broad implementation commitment made before its dependencies are visible.


