- Release status
- Need help?
- Getting started
- Usage guide
- Configuration reference
- Building the SDK
- Contributing
This repository contains the Okta management SDK for .NET. This SDK can be used in your server-side code to interact with the Okta management API and:
- Create and update users with the Users API
- Add security factors to users with the Factors API
- Manage groups with the Groups API
- Manage applications with the Apps API
- Manage logs with the Logs API
- Manage sessions with the Sessions API
- Manage templates with the Custom Templates API
- Manage identity providers with the Identity Providers API
- Manage authorization servers with the Authorization Servers API
- Manage event hooks with the Event Hooks Management API
- Manage inline hooks with the Inline Hooks Management API.
- Manage features with the Features API.
- Manage linked objects with the Linked Objects API.
- Manage trusted origins with the Trusted Origins API.
- Manage user types with the User Types API.
- Manage custom domains with the Domains API.
- Manage network zones with the Zones API's endpoints.
- Much more!
Note: For more details about the APIs and models the SDK support, check out the API docs
We also publish these other libraries for .NET:
You can learn more on the Okta + .NET page in our documentation.
This library uses semantic versioning and follows Okta's library version policy.
✔️ The current stable major version series is: 6.x
✔️ The 5.x series is retiring on June 8th 2023. Until then, we will only fix high-risk security vulnerabilities and other issues will be reviewed on a case-by-case basis. New APIs will be added only on series 6.x, but you can still use the 5.x series to call any new endpoint. The SDK will be still available on Nuget, and the source-code is located in the legacy-5.x-series
branch. Please, reach out to the Okta Customer Support Team at developers@okta.com if you have any questions or issues.
Version | Status |
---|---|
7.x | ✔️ Stable (migration guide) |
6.x |
The latest release can always be found on the releases page. For more information about our SDKs' lifecycle, check out our docs.
If you run into problems using the SDK, you can
- Ask questions on the Okta Developer Forums
- Post issues here on GitHub (for code errors)
The SDK is compatible with:
- .NET Standard 2.0
- .NET Framework 4.6.1 or higher
- .NET Core 3.0 or higher
- .NET 5.0 or higher
Visual Studio 2017 or newer is required as previous versions are not compatible with the above frameworks.
- Right-click on your project in the Solution Explorer and choose Manage Nuget Packages...
- Search for Okta. Install the
Okta.Sdk
package.
Simply run install-package Okta.Sdk
. Done!
You'll also need:
- An Okta account, called an organization (sign up for a free developer organization if you need one)
- An API token
Construct a client instance by passing it your Okta domain name and API token:
using System.Collections.Generic;
using System.Diagnostics;
using Okta.Sdk.Api;
using Okta.Sdk.Client;
using Okta.Sdk.Model;
namespace Example
{
public class Example
{
public static void Main()
{
Configuration config = new Configuration();
config.OktaDomain = "https://your-subdomain.okta.com";
// Configure API key authorization: API_Token
config.Token.Add("Authorization", "YOUR_API_KEY");
var apiInstance = new AgentPoolsApi(config);
var poolId = "poolId_example"; // string | Id of the agent pool for which the settings will apply
var updateId = "updateId_example"; // string | Id of the update
try
{
// Activate an Agent Pool update
AgentPoolUpdate result = apiInstance.ActivateAgentPoolsUpdate(poolId, updateId);
Debug.WriteLine(result);
}
catch (ApiException e)
{
Debug.Print("Exception when calling AgentPoolsApi.ActivateAgentPoolsUpdate: " + e.Message );
Debug.Print("Status Code: "+ e.ErrorCode);
Debug.Print(e.StackTrace);
}
}
}
}
Hard-coding the Okta domain and API token works for quick tests, but for real projects you should use a more secure way of storing these values (such as environment variables). This library supports a few different configuration sources, covered in the configuration reference section.
To use the API client with an HTTP proxy, you can either setup your proxy via different configuration sources, covered in the configuration reference section, or via API constructor. If you have both, the proxy passed via constructor will take precedence.
System.Net.WebProxy webProxy = new System.Net.WebProxy("http://myProxyUrl:80/");
webProxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
var appsApi = new ApplicationApi(webProxy : webProxy);
Okta allows you to interact with Okta APIs using scoped OAuth 2.0 access tokens. Each access token enables the bearer to perform specific actions on specific Okta endpoints, with that ability controlled by which scopes the access token contains.
This SDK supports this feature only for service-to-service applications. Check out our guides to learn more about how to register a new service application using a private and public key pair.
When using this approach you won't need an API Token because the SDK will request an access token for you. In order to use OAuth 2.0, construct an API client instance by passing the following parameters:
var oauthAppsApi = new ApplicationApi(new Configuration
{
OktaDomain = "https://{{yourOktaDomain}}",
AuthorizationMode = AuthorizationMode.PrivateKey,
ClientId = "{{clientId}}",
Scopes = new List<string> { "okta.users.read", "okta.apps.read" }, // Add all the scopes you need
PrivateKey = new JsonWebKeyConfiguration(jsonString)
});
Key object for assigning to the PrivateKey can be created and initialized inline like in this example for RSA key:
var privateKey = new JsonWebKeyConfiguration
{
P = "{{P}}",
Kty = "RSA",
Q = "{{Q}}",
D = "{{D}}",
E = "{{E}}",
Kid = "{{P}}",
Qi = "{{Qi}}"
};
var configuration = new Configuration
{
OktaDomain = "https://{{yourOktaDomain}}",
AuthorizationMode = AuthorizationMode.PrivateKey,
ClientId = "{{clientId}}",
Scopes = new List<string> { "okta.users.read", "okta.apps.read" }, // Add all the scopes you need
PrivateKey = privateKey
};
var oauthAppsApi = new ApplicationApi(configuration);
It is possible to use an access token you retrieved outside of the SDK for authentication. For that, set Configuration.AuthorizationMode
configuration property to AuthorizationMode.BearerToken
and Configuration.AccessToken
to the token string.
These examples will help you understand how to use this library. You can also browse the full API reference documentation.
Once you initialize an API client, you can call methods to make requests to the Okta API.
// Get the user with a user ID or login
var user = await userApi.GetUserAsync("<Some user ID or login>");
The string argument for GetUserAsync
can be the user's ID or the user's login (usually their email).
The SDK will automatically paginate Okta collections for you:
// These different styles all perform the same action:
var allUsers = await userApi.ListUsers().ToListAsync();
var allUsers = await userApi.ListUsers().ToArrayAsync();
var foundUsers = await userApi
.ListUsers(search: $"profile.nickName eq \"Skywalker\"")
.ToArrayAsync();
// Create a user with the specified password
var createUserRequest = new CreateUserRequest
{
Profile = new UserProfile
{
FirstName = "Anakin",
LastName = "Skywalker",
Email = "darth.vader@imperial-senate.gov",
Login = "darth.vader@imperial-senate.gov",
},
Credentials = new UserCredentials
{
Password = new PasswordCredential
{
Value = "D1sturB1ng"
}
}
};
var createdUser = await _userApi.CreateUserAsync(createUserRequest);
// Activate the user
await _userApi.ActivateUserAsync(createdUser.Id, false);
// Update profile
createdUser.Profile.NickName = nickName;
var updateUserRequest = new UpdateUserRequest
{
Profile = createdUser.Profile
};
var updatedUser = await _userApi.UpdateUserAsync(createdUser.Id, updateUserRequest);
You can't create attributes via code right now, but you can get and set their values. To create them you have to use the Profile Editor in the Developer Console web UI. Once you have created them, you can use the code below:
user.Profile.AdditionalProperties = new Dictionary<string, object>();
user.Profile.AdditionalProperties["homeworld"] = "Planet Earth";
var updateUserRequest = new UpdateUserRequest
{
Profile = user.Profile
};
var updatedUser = await _userApi.UpdateUserAsync(createdUser.Id, updateUserRequest);
var userHomeworld = updatedUser.Profile.AdditionalProperties["homeworld"];
await _userApi.DeactivateOrDeleteUserAsync(createdUser.Id);
// List all applications
var appList = await _applicationApi.ListApplications().ToArrayAsync();
var createdApp = await _applicationApi.CreateApplicationAsync(new CreateBasicAuthApplicationOptions()
{
Label = "Sample Basic Auth App",
Url = "https://example.com/login.html",
AuthUrl = "https://example.com/auth.html",
});
var retrievedById = await _applicationApi.GetApplicationAsync(createdApp.Id);
var app = new OpenIdConnectApplication
{
Name = "oidc_client",
SignOnMode = "OPENID_CONNECT",
Label = $"dotnet-sdk: AddOpenIdConnectApp",
Credentials = new OAuthApplicationCredentials()
{
OauthClient = new ApplicationCredentialsOAuthClient()
{
ClientId = testClientId,
TokenEndpointAuthMethod = "client_secret_post",
AutoKeyRotation = true,
},
},
Settings = new OpenIdConnectApplicationSettings
{
OauthClient = new OpenIdConnectApplicationSettingsClient()
{
ClientUri = "https://example.com/client",
LogoUri = "https://example.com/assets/images/logo-new.png",
ResponseTypes = new List<string>
{
"token",
"id_token",
"code",
},
RedirectUris = new List<string>
{
"https://example.com/oauth2/callback",
"myapp://callback",
},
PostLogoutRedirectUris = new List<string>
{
"https://example.com/postlogout",
"myapp://postlogoutcallback",
},
GrantTypes = new List<string>
{
"implicit",
"authorization_code",
},
ApplicationType = "native",
TosUri = "https://example.com/client/tos",
PolicyUri = "https://example.com/client/policy",
},
}
};
var createdApp = await _applicationApi.CreateApplicationAsync(app);
Collections can be fetched with manually controlled pagination, see the following.
var retrievedUsers = new List<IUser>();
var users = _userApi.ListUsers(limit: 5); // 5 records per a page
var enumerator = users.GetPagedEnumerator();
while (await enumerator.MoveNextAsync())
{
retrievedUsers.AddRange(enumerator.CurrentPage.Items);
// ....................
}
Note: For more API samples checkout our tests
The Okta API will return 429 responses if too many requests are made within a given time. Please see Rate Limiting at Okta for a complete list of which endpoints are rate limited. When a 429 error is received, the X-Rate-Limit-Reset
header will tell you the time at which you can retry. This section discusses methods for handling rate limiting with this SDK.
This SDK uses the built-in retry strategy to automatically retry on 429 errors. You can use the default configuration options for the built-in retry strategy, or provide your desired values via client configuration.
You can configure the following options when using the built-in retry strategy:
Configuration Option | Description |
---|---|
RequestTimeout | The waiting time in milliseconds for a request to be resolved by the client. Less than or equal to 0 means "no timeout". The default value is 0 (None). |
MaxRetries | The number of times to retry. |
Check out the Configuration Reference section for more details about how to set these values via configuration.
You can implement your own retry strategy via Polly, and assign it to the RetryConfiguration.AsyncPolicy
property.
AsyncPolicy<IRestResponse> retryAsyncPolicy = Policy
.Handle<ApiException>(ex => ex.ErrorCode == 429)
.OrResult<IRestResponse>(r => (int)r.StatusCode == 429)
.WaitAndRetryAsync(configuration.MaxRetries.Value,
sleepDurationProvider: (retryAttempt, response,
context) => MyCalculateDelayMethod(retryAttempt, response, context)
);
RetryPolicy.AsyncPolicy = retryAsyncPolicy;
You will have to read the X-Rate-Limit-Reset
header on the 429 response. This will tell you the time at which you can retry. Because this is an absolute time value, we recommend calculating the wait time by using the Date
header on the response, as it is in sync with the API servers, whereas your local clock may not be. We also recommend adding 1 second to ensure that you will be retrying after the window has expired (there may be a sub-second relative time skew between the X-Rate-Limit-Reset
and Date
headers).
This library looks for configuration in the following sources:
- An
okta.yaml
file in a.okta
folder in the current user's home directory (~/.okta/okta.yaml
or%userprofile%\.okta\okta.yaml
) - An
appsettings.json
file in the application or project's root directory - An
okta.yaml
file in a.okta
folder in the application or project's root directory - Environment variables
- Configuration explicitly passed to the constructor (see the example in Getting started)
Higher numbers win. In other words, configuration passed via the constructor will override configuration found in environment variables, which will override configuration in okta.yaml
(if any), and so on.
Note that json
files cannot be used if they contain JavaScript comments. Comments are not allowed by JSON format.
When you use an API Token instead of OAuth 2.0 the full YAML configuration looks like:
okta:
client:
connectionTimeout: 30000 # milliseconds
oktaDomain: "https://{yourOktaDomain}"
proxy:
port: null
host: null
username: null
password: null
token: {apiToken}
requestTimeout: 0 # milliseconds
rateLimit:
maxRetries: 4
When you use OAuth 2.0 the full YAML configuration looks like this when using EC key:
okta:
client:
connectionTimeout: 30000 # milliseconds
oktaDomain: "https://{yourOktaDomain}"
proxy:
port: null
host: null
username: null
password: null
authorizationMode: "PrivateKey"
clientId: "{yourClientId}"
Scopes:
- scope1
- scope2
PrivateKey: # This SDK supports both RSA and EC keys.
kty: "EC"
crv: "P-256"
x: "{x}"
y: "{y}"
requestTimeout: 0 # milliseconds
rateLimit:
maxRetries: 4
Or like this for RSA key:
okta:
client:
connectionTimeout: 30000 # milliseconds
oktaDomain: "https://{yourOktaDomain}"
proxy:
port: null
host: null
username: null
password: null
authorizationMode: "PrivateKey"
clientId: "{yourClientId}"
Scopes:
- scope1
- scope2
PrivateKey:
"p": "{p}"
"kty": "RSA"
"q": "{q}"
"d": "{d}"
"e": "{e}"
"kid": "{kid}"
"qi": "{qi}"
requestTimeout: 0 # milliseconds
rateLimit:
maxRetries: 4
Each one of the configuration values above can be turned into an environment variable name with the _
(underscore) character:
OKTA_CLIENT_CONNECTIONTIMEOUT
OKTA_CLIENT_TOKEN
- and so on
In most cases, you won't need to build the SDK from source. If you want to build it yourself just clone the repo and compile using Visual Studio.
We're happy to accept contributions and PRs! Please see the contribution guide to understand how to structure a contribution.