This library uses semantic versioning and follows Okta's library version policy. In short, we don't make breaking changes unless the major version changes!
There are a number of changes to be aware of when migrating from version 8 to version 9.
When you change your project to reference version 9 instead of 8, reference the following to help correct compilation errors:
- ApiTokenApi
- ListApiTokens
- parameters removed.
- ListApiTokens
- ApplicationTokensApi
- ListOAuth2TokensForApplication
- return type OAuth2Token changed to OAuth2RefreshToken.
- GetOAuth2TokenForApplication
- return type OAuth2Token changed to OAuth2RefreshToken.
- ListOAuth2TokensForApplication
- ApplicationUsersApi
- AssignUserToApplication
- parameter of type AppUser changed to AppUserAssignRequest.
- UpdateApplicationUser
- parameter of type AppUser changed to AppUserUpdateRequest.
- AssignUserToApplication
- AuthenticatorApi
- ListAuthenticators
- return type Authenticator changed to AuthenticatorBase
- ListAuthenticators
- AuthorizationServerKeysApi
- ListAuthorizationServerKeys
- return type collection of JsonWebKey changed to collection of AuthorizationServerJsonWebKey.
- RotateAuthorizationServerKeys
- return type collection of JsonWebKey changed to collection of AuthorizationServerJsonWebKey.
- ListAuthorizationServerKeys
- CustomTemplatesApi
- ListEmailTemplates
- return type EmailTemplate changed to EmailTemplateResponse.
- GetEmailTemplate
- return type EmailTemplate changed to EmailTemplateResponse.
- GetEmailSettings
- return type EmailSettings changed to EmailSettingsResponse.
- ListEmailTemplates
- ThemesApi
- ReplaceBrandTheme
- parameter of type Theme changed to UpdateThemeRequest
- ReplaceBrandTheme
- DeviceApi
- ListDevices
- return type collection of Device changed to collection of DeviceList.
- ListDevices
- ApplicationConnectionsApi
- GetDefaultProvisioningConnectionForApplication
- return type ProvisioningConnection changed to ProvisioningConnectionResponse
- UpdateDefaultProvisioningConnectionForApplication
- return type ProvisioningConnection changed to ProvisioningConnectionResponse
- GetDefaultProvisioningConnectionForApplication
- AuthenticatorApi methods that previously returned Authenticator now return AuthenticatorBase.
- GroupApi
- ListGroupUsers
- return type collection of User changed to collectio of GroupMember.
- ListGroupUsers
- RealmApi
- CreateRealm
- parameter of type Realm changed to CreateRealmRequest.
- CreateRealm
- UserFacorApi
- ResendEnrollFactor
- parameter of type UserFactor changed to ResendUserFactor.
- return type UserFactor changed to ResendUserFactor.
- GetFactorTransactionStatus
- return type VerifyUserFactorResponse changed to UserFactorPushTransaction
- VerifyFactor
- parameter of type VerifyFactorRequest changed to UserFactorVerifyRequest
- return type VerifyUserFactorResponse changed to UserFactorVerifyResponse
- ResendEnrollFactor
Some functionality has moved from generic high level API classes to more specific API classes.
- AuthorizationServerApi functionality is now broken out into more specific API classes.
- GroupOwnerApi contains functionality previously in GroupsApi.
See also added.
The following are methods and classes that have been replaced and the methods or classes that replaced them.
- CustomizationApi is replaced by CustomTemplatesApi, CusomPagesApi and BrandsApi, ThemesApi see added.
- RealmApi.UpdateRealm is replaced by RealmApi.ReplaceRealm.
- ProvisioningConnection is replaced by ProvisioningConnectionRequest & ProvisioningConnectionResponse.
- VerifyFactorRequest is replaced by UserFactorVerifyRequest
- VerifyUserFactorResponse is replaced by UserFactorVerifyResponse
The following are methods that are removed in version 9:
- SchemaApi methods removed:
- GetAppUISchemaLinksAsync
- UserApi methods removed:
- SetLinkedObjectForUser
The following are methods and APIs that are added in version 9:
- ApiTokenApi methods added:
- UpsertApiToken
- ApplicationConnectionsApi methods added:
- VerifyProvisioningConnectionForApplication
- AuthorizationAssocApi is a new API to maange authorization server associations.
- AuthorizationServerClaimsApi is a new API to manage authorization server claims.
- AuthroziationServerClientsApi is a new API to manage authorization server clients.
- AuthorizationServerKeysApi is a new API to manage authorization server keys.
- AuthorizationServerPoliciesApi is a new API to manage authorization server policies.
- AuthorizationServerRulesApi is a new API to manage authorization server rules.
- AuthorizationServerScopesApi is a new API to manage authorization server scopes.
- ApplicationGroupsApi methods added:
- UpdateGroupAssignmentToApplication overload accepting a list of JsonPathOperation objects.
- BrandsApi is a new API to manage brands.
- CustomTemplatesApi is new API to manage custom templates.
- CustotmPagesApi is new API to manage custom pages.
- DirectoriesIntegrationApi is a new API to manage AD integrations.
- GroupOwnerApi is a new API to manage group owners.
- InlineHookApi methods added:
- UpdateInlineHook
- OktaApplicationSettingsApi is a new API to manage Okta application settings.
- ThemesApi is a new API to manage themes.
- OrgSettingApi methods added:
- GetThirdPartyAdminSetting
- UpdateThirdPartyAdminSetting
- GetClientPrivilegesSetting
- AssignClientPrivilegesSetting
- RealAssignmentApi is a new API to manage realm assignments.
- SSFReceiverApi is a new API to manage the consumption of security events.
- SSFSecurityEventTokenApi is a new API to manage security event tokens.
- SSFTransmitterApi is a new API to manage security event transmitters.
- SessionApi methods added:
- GetCurrentSession
- CloseCurrentSession
- RefreshCurrentSession
- UserApi methods added:
- ReplaceLinkedObjectForUser
- ListLinkedObjectsForUser
- DeleteLinkedObjectForUser
- AttackProtectionApi methods added:
- GetAuthenticatorSettings
- ReplaceAuthenticatorSettings
- RoleAssignmentApi methods added:
- ListRolesForClient
- AssignRoleToClient
We have upgraded the Okta OpenAPI specifications which caused a few breaking changes due to schema changes and bug fixes in the spec.
IdentityProvider.Links
now returns an instance of IdentityProviderLinks
class instead of LinksSelf
. The IdentityProviderLinks
provides several predefined properties that were missing in 7.x; you can also access not-predefined properties via additionalProperties
:
Before
var selfLinkHref = idp.Links.Self.Href;
Now
Get the Self
link is the same as before:
var selfLink = idp.Links.Self.Href;
The following assertions showcase what other properties are accessible via Links
, and what they look like:
idp.Links.Metadata.Href.Should().Be("https://{yourOktaDomain}/api/v1/idps/0oa1k5d68qR2954hb0g4/metadata.xml");
idp.Links.Metadata.Hints.Allow.Any(x => x == HttpMethod.GET).Should().BeTrue();
idp.Links.Metadata.Type.Should().Be("application/xml");
idp.Links.Acs.Href.Should().Be("https://{yourOktaDomain}/sso/saml2/0oa1k5d68qR2954hb0g4");
idp.Links.Acs.Hints.Allow.Any(x => x == HttpMethod.POST).Should().BeTrue();
idp.Links.Acs.Type.Should().Be("application/xml");
idp.Links.Users.Href.Should().Be("https://{yourOktaDomain}/api/v1/idps/0oa1k5d68qR2954hb0g4/users");
idp.Links.Users.Hints.Allow.Any(x => x == HttpMethod.GET).Should().BeTrue();
idp.Links.Activate.Href.Should().Be("https://{yourOktaDomain}/api/v1/idps/0oa1k5d68qR2954hb0g4/lifecycle/activate");
idp.Links.Activate.Hints.Allow.Any(x => x == HttpMethod.POST).Should().BeTrue();
idp.Links.Deactivate.Href.Should().Be("https://{yourOktaDomain}/api/v1/idps/0oa1k5d68qR2954hb0g4/lifecycle/deactivate");
idp.Links.Deactivate.Hints.Allow.Any(x => x == HttpMethod.POST).Should().BeTrue();
idp.Links.Authorize.Href.Should().Be("https://testorg.com/oauth2/v1/authorize?idp=foo");
idp.Links.Authorize.Hints.Allow.Any(x => x == HttpMethod.GET).Should().BeTrue();
idp.Links.Authorize.Templated.Should().BeTrue();
idp.Links.ClientRedirectUri.Href.Should().Be("https://testorg.com/oauth2/v1/authorize/callback");
idp.Links.ClientRedirectUri.Hints.Allow.Any(x => x == HttpMethod.POST).Should().BeTrue();
To access non-predefined links you can check the AdditionalProperties
property:
var undefinedLink = (JObject)idp.Links.AdditionalProperties["undefinedLink"];
var href = undefinedLink["href"].ToString();
Note: This change fixed #700
UserSchemaAttribute.MinLength
and UserSchemaAttribute.MaxLength
are now int?
instead of int
.
Note: This change fixed #713.
Adding support for DPoP required a few breaking changes to make the code clearer.
- The
IJwtGenerator
interface was replaced byIClientAssertionJwtGenerator
- The
IOAuthTokenProvider
has been updated with the following changes and the default implementation has been adapted:- The
Task<string> GetAccessTokenAsync(bool forceRenew = false, CancellationToken cancellationToken = default);
method has been replaced byTask<OAuthTokenResponse> GetAccessTokenResponseAsync(bool forceRenew = false, CancellationToken cancellationToken = default);
- It now exposes
Task AddOrUpdateAuthorizationHeader(RequestOptions requestOptions string requestUri, string httpMethod, CancellationToken cancellationToken = default);
andstring GetDpopProofJwt(String? nonce = null, String? httpMethod = null, String? uri = null, String? accessToken = null);
methods
- The
- The
OAuthApi
constructors have been updated with the following changes:- The
public OAuthApi(Okta.Sdk.Client.Configuration configuration = null, IJwtGenerator jwtGenerator = null)
constructor has been replaced bypublic OAuthApi(Okta.Sdk.Client.Configuration configuration = null, IClientAssertionJwtGenerator clientAssertionJwtGenerator = null, IDpopProofJwtGenerator dpopProofJwtGenerator = null)
- The
public OAuthApi(Okta.Sdk.Client.IAsynchronousClient asyncClient, Okta.Sdk.Client.IReadableConfiguration configuration, IJwtGenerator jwtGenerator)
constructor has been replaced bypublic OAuthApi(Okta.Sdk.Client.IAsynchronousClient asyncClient, Okta.Sdk.Client.IReadableConfiguration configuration, IClientAssertionJwtGenerator clientAssertionJwtGenerator, IDpopProofJwtGenerator dpopProofJwtGenerator)
- The
If you have your own implementations of the updated interfaces, you will have to make the corresponding changes and implement the new exposed methods. You can check the DefaultOAuthTokenProvider
and DefaultClientAssertionJwtGenerator
classes for more details on how to implement the new/updated interfaces.
The Okta Engineering team decided to roll back the latest changes in the Policy OpenAPI specification, which were released in v7.0.6. Hence, the schema PolicyCanBeCreatedOrReplaced
has been removed, and all the methods that received and/or returned an instance of PolicyCanBeCreatedOrReplaced
will now accept and/or return an instance of the Policy
schema. The obsolete tag has been removed from the corresponding methods.
Before in v7.0.6
System.Threading.Tasks.Task<PolicyCanBeCreatedOrReplaced> CreatePolicyAsync( PolicyCanBeCreatedOrReplaced policy , bool? activate = default(bool?) , System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
System.Threading.Tasks.Task<ApiResponse<PolicyCanBeCreatedOrReplaced>> CreatePolicyWithHttpInfoAsync( PolicyCanBeCreatedOrReplaced policy , bool? activate = default(bool?) , System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
System.Threading.Tasks.Task<Policy> ReplacePolicyAsync(string policyId, Policy policy, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
System.Threading.Tasks.Task<ApiResponse<Policy>> ReplacePolicyWithHttpInfoAsync(string policyId, Policy policy, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
Now
Note: The obsolete tag has been removed for the following methods.
System.Threading.Tasks.Task<Policy> CreatePolicyAsync(Policy policy, bool? activate = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
System.Threading.Tasks.Task<ApiResponse<Policy>> CreatePolicyWithHttpInfoAsync(Policy policy, bool? activate = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
System.Threading.Tasks.Task<Policy> ReplacePolicyAsync(string policyId, Policy policy, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
System.Threading.Tasks.Task<ApiResponse<Policy>> ReplacePolicyWithHttpInfoAsync(string policyId, Policy policy, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
Obsolete methods and relations have been removed for the IdentityProviderPolicy
schema, which no longer inherits from Policy.
We have upgraded the RestSharp dependency from 106.13.0
to 110.2.0
. This caused a few breaking changes due to the removal and changes of interfaces. You can learn more about RestSharp changes here.
IOAuthTokenProvider.GetOAuthRetryPolicy()
now returnsRestResponse
instead ofIRestResponse
. This has impactedDefaultOAuthTokenProvider
.RetryConfiguration.AsyncRetryPolicy
now receives/returnsAsyncPolicy<RestResponse>
instead ofAsyncPolicy<IRestResponse>
RetryConfiguration.GetRetryPolicy
has changed its interface from
public static Polly.AsyncPolicy<IRestResponse> GetRetryPolicy(IReadableConfiguration configuration, Func<DelegateResult<IRestResponse>, TimeSpan, int, Context, Task> onRetryAsyncFunc = null)
to
public static Polly.AsyncPolicy<RestResponse> GetRetryPolicy(IReadableConfiguration configuration, Func<DelegateResult<RestResponse>, TimeSpan, int, Context, Task> onRetryAsyncFunc = null)
RetryConfiguration.AddRetryHeaders
has changed its interface from
public static void AddRetryHeaders (Context context, IRestRequest request)
to
public static void AddRetryHeaders (Context context, RestRequest request)
ApiClient.InterceptRequest
partial method now receiveRestRequest
instead ofIRestRequest
ApiClient.InterceptResponse
partial method now receiveRestResponse
instead ofIRestResponse
We have upgraded the Okta management OpenAPI specification to be aligned with the Okta release v2023.07.0. As part of a naming standardization performed by the API teams, a few methods have changed their name in order to use unified prefixes. For example:
-
If an operation requires a PUT request that replaces a resource with the passed values, the method will now use the
Replace
prefix instead ofUpdate
-
if the method performs a partial update on a specific resource, the method will now use the
PartialUpdate
prefix instead ofUpdate
-
If the operation's prefix started with
Add
, it now starts withCreate
Some assigments and factors operations have also changed the prefix, for example:
CreateApplicationGroupAssignmentAsync
is nowAssignGroupToApplicationAsync
DeleteApplicationGroupAssignmentAsync
is nowUnassignApplicationFromGroupAsync
DeleteFactorAsync
is nowUnenrollFactorAsync
Also, some operations have been moved to their own API Client. For example, multiple operations that corresponded to an application's resource and have been part of the ApplicationsApi
, are now available in their own application-resource API client:
- ApplicationTokensApi
- ApplicationLogosApi
- ApplicationUsersApi
- ApplicationGroupsApi
- ApplicationCredentialsApi
- ApplicationGrantsApi
- ApplicationConnectionsApi
- ApplicationUsersApi
Additionally, the method UserApi.DeactivateOrDeleteUserAsync
has been split into two separate methods: UserApi.DeactivateUserAsync
and UserApi.DeleteUserAsync
.
ProfileMappingApi.UpdateProfileMappingAsync
now receives an ProfileMappingRequest
param instead of a Mapping
param:
Before
var mapping = await _profileMappingApi.GetProfileMappingAsync(mappings.FirstOrDefault().Id);
// Add properties
if (mapping.Properties == null)
{
mapping.Properties = new Dictionary<string, ProfileMappingProperty>();
}
mapping.Properties.Add("userType", new ProfileMappingProperty
{
Expression = "appuser.firstName",
PushStatus = ProfileMappingPropertyPushStatus.PUSH,
});
mapping.Properties.Add("nickName", new ProfileMappingProperty
{
Expression = "appuser.firstName + appuser.lastName",
PushStatus = ProfileMappingPropertyPushStatus.PUSH,
});
var updatedMapping = await _profileMappingApi.UpdateProfileMappingAsync(mapping.Id, mapping);
Now
var profileMappingRequest = new ProfileMappingRequest();
profileMappingRequest.Properties = new Dictionary<string, ProfileMappingProperty>();
profileMappingRequest.Properties.Add("userType", new ProfileMappingProperty
{
Expression = "appuser.firstName",
PushStatus = ProfileMappingPropertyPushStatus.PUSH,
});
profileMappingRequest.Properties.Add("nickName", new ProfileMappingProperty
{
Expression = "appuser.firstName + appuser.lastName",
PushStatus = ProfileMappingPropertyPushStatus.PUSH,
});
var updatedMapping = await _profileMappingApi.UpdateProfileMappingAsync(mapping.Id, profileMappingRequest);
Note: For more details about API changes, check out the latest changes on the Okta OpenAPI specification.
As a general rule, we try to avoid inline enums. However, this version of the OpenAPI specification contains a few inline enums.
The following obsolete methods have been removed:
- UserApi
[Obsolete("This method is obsolete. Use UpdateUserAsync(string userId, User user,...) instead.")]
System.Threading.Tasks.Task<User> UpdateUserAsync(string userId, UpdateUserRequest user, bool? strict = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
[Obsolete("This method is obsolete. Use UpdateUserWithHttpInfoAsync(string userId, User user,...) instead.")]
System.Threading.Tasks.Task<ApiResponse<User>> UpdateUserWithHttpInfoAsync(string userId, UpdateUserRequest user, bool? strict = default(bool?), System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
- RoleApi
[Obsolete("This method is obsolete. Use CreateRoleAsync(CreateIamRoleRequest,...) instead.")]
System.Threading.Tasks.Task<IamRole> CreateRoleAsync(IamRole instance, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
[Obsolete("This method is obsolete. Use CreateRoleWithHttpInfoAsync(CreateIamRoleRequest,...) instead.")]
System.Threading.Tasks.Task<ApiResponse<IamRole>> CreateRoleWithHttpInfoAsync(IamRole instance, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
[Obsolete("This method is obsolete. Use ReplaceRoleAsync(string roleIdOrLabel, UpdateIamRoleRequest,...) instead.")]
System.Threading.Tasks.Task<IamRole> ReplaceRoleAsync(string roleIdOrLabel, IamRole instance, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
[Obsolete("This method is obsolete. Use ReplaceRoleWithHttpInfoAsync(string roleIdOrLabel, UpdateIamRoleRequest,...) instead.")]
System.Threading.Tasks.Task<ApiResponse<IamRole>> ReplaceRoleWithHttpInfoAsync(string roleIdOrLabel, IamRole instance, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken));
- ResourceSetApi
[Obsolete("This method is obsolete and will be removed in the next major release. Use CreateResourceSetAsync(CreateResourceSetRequest instance...)")]
public async System.Threading.Tasks.Task<ResourceSet> CreateResourceSetAsync(ResourceSet instance, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
[Obsolete("This method is obsolete and will be removed in the next major release. Use CreateResourceSetWithHttpInfoAsync(CreateResourceSetRequest instance...)")]
public async System.Threading.Tasks.Task<Okta.Sdk.Client.ApiResponse<ResourceSet>> CreateResourceSetWithHttpInfoAsync(ResourceSet instance, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
Model.IsRequired
attribute is now Default
to avoid JSON serialization runtime issues.
In releases prior to version 6 we use an Open API v2 specification, and an Okta custom client generator to partially generate our SDK. A new version of the Open API specification (V3) has been released, and new well-known generators are now available and well received by the community. Planning the future of this SDK, we consider this a good opportunity to modernize by aligning with established standards for API client generation.
We acknowledge that migrating from v5 to v6 will require considerable effort, but we expect this change to benefit our customers in the long term.
As it's mentioned above, in previous versions we used an OpenAPI v2 specification with many custom attributes and vendor extensions that required us to implement a custom client generator that should work for all the languages Okta supports. Our process to release support for new APIs and endpoints could have been better since, most of the time we needed to add new APIs, the generator required some adjustments to support new attributes added to the spec. This process caused delays in releases that could be available for our customers sooner.
With OpenAPI v3, we saw an opportunity for improvement in several areas:
- We can provide an API specification that follows the OpenAPI v3 standard and eliminate custom attributes and vendor extensions.
- Given that our specification is now compliant with OASv3, we can use well-known generators used and maintained by the community. In this case, we chose openapi-generator.tech.
- Given that we eliminated custom attributes, we can speed up our release process and let our customers access new APIs sooner. Also, everyone will have access to the OpenAPI specification and will be able to generate their own clients in other languages of their choice.
In releases prior to version 6, you would instantiate a global OktaClient
and access specific API clients via its properties. Now, each API has its own client and you only instantiate those clients you are interested in:
Before:
var oktaClient = new OktaClient();
var apps = await oktaClient.Applications.ListApplications().ToListAsync();
Now:
var appApiClient = new ApplicationApi();
var apps = await appApiClient.ListApplications().ToListAsync();
Note: Check out the SDK tests to see more 6.x APIs examples.
In order to implement DI, you have to register your APIs in the Dependency Injection Container:
Before:
1- Register your OktaClient
in the Startup.cs
or Program.cs
file.
// Startup.cs or Program.cs
// ...
// This sample uses OAuth but you can also use your API Token
builder.Services.AddScoped<IOktaClient>(_ => new OktaClient(
new Configuration
{
OktaDomain = "https://myOktaDomain.com/",
Scopes = new List<string> { "okta.users.read" },
ClientId = "CLIENT_ID",
AuthorizationMode = AuthorizationMode.PrivateKey,
PrivateKey = new JsonWebKeyConfiguration("JSON_PRIVATE_KEY"),
}));
var app = builder.Build();
2- Inject your OktaClient
in your controllers or Minimal APIs
app.MapGet("/users", async (IOktaClient oktaClient) =>
{
return await oktaClient.Users.ListUsers().ToListAsync();
});
Now:
1- Register your APIs in the Startup.cs
or Program.cs
file.
// Startup.cs or Program.cs
// ...
// This sample uses OAuth but you can also use your API Token
builder.Services.AddScoped<IUserApi>(_ => new UserApi(
new Configuration
{
OktaDomain = "https://myOktaDomain.com/",
Scopes = new HashSet<string> { "okta.users.read" },
ClientId = "CLIENT_ID",
AuthorizationMode = AuthorizationMode.PrivateKey,
PrivateKey = new JsonWebKeyConfiguration("JSON_PRIVATE_KEY"),
}));
var app = builder.Build();
2- Inject your APIs in your controllers or Minimal APIs
app.MapGet("/users", async (IUserApi api) =>
{
return await api.ListUsers().ToListAsync();
});
Note: Consider registering groups of services if you need to configure DI for multiple APIs.
Unlike previous versions, ConnectionTimeout
and RequestTimeout
should now expressed in milliseconds. Also, Scopes
is now a HashSet<string>
to avoid duplicated entries.
StringEnums
are still supported. However, format might slightly change depending on the OpenAPI specification and codegen.
In previous versions, all models inherited from Resource
to facilitate the JSON serialization process and manipulate raw data. In series 6.x, this is no longer the case; The SDK now uses the JSON SubTypes dependency.
Models that support dynamic properties now expose the AdditionalProperties
property:
Before:
user.Profile["homeworld"] = "Tattooine";
Now:
user.Profile.AdditionalProperties = new Dictionary<string, object>();
user.Profile.AdditionalProperties["homeworld"] = "Planet Earth";
In previous versions, you had to manipulate raw data via the Resource
convenience methods to access Links
. Now, Links
are exposed are standard properties:
Before:
var accessPolicyHref = createdApp.GetProperty<Resource>("_links")?
.GetProperty<Resource>("accessPolicy")?
.GetProperty<string>("href");
Now:
var accessPolicyHref = createdApp.Links.AccessPolicy.Href;
In previous versions, the OktaClient provided a generic Get
method where devs could provide a specifc type to cast a model. Now, you have to cast a model after a Get
method using as
:
Before:
var retrievedApp = await client.Applications.GetApplicationAsync<IBookmarkApplication>(createdApp.Id);
Now:
var retrievedApp = await _applicationApi.GetApplicationAsync(createdApp.Id) as BookmarkApplication;
The now SDK throws an ApiException
every time the server responds with an invalid status code, or there is an internal error.
Before:
try
{
// ...
var retrievedApp = await client.Applications.GetApplicationAsync<IBookmarkApplication>("unknownId");
}
catch (OktaApiException apiException)
{
var message = apiException.Message; //"Not found: Resource not found: foo (AppInstance) (404, E0000007)"
var statusCode = apiException.StatusCode; //404
var errorSummary = apiException.ErrorSummary; //"Not found: Resource not found: foo (AppInstance)"
var errorId = apiException.ErrorId; //"E0000007"
var errorCode = apiException.ErrorCode; //"E0000007"
}
Now:
try
{
//...
var retrievedApp = await _applicationApi.GetApplicationAsync("unknownId") as BookmarkApplication;
}
catch (ApiException apiException)
{
var message = apiException.Message; //Error calling GetApplication: {"errorCode":"E0000007","errorSummary":"Not found: Resource not found: foo (AppInstance)","errorLink":"E0000007","errorId":"oaeWzp-a2A0TCOm7D0FtnHcbg","errorCauses":[]}
var statusCode = apiException.ErrorCode; //404
var errorContent = apiException.ErrorContent; //{"errorCode":"E0000007","errorSummary":"Not found: Resource not found: foo (AppInstance)","errorLink":"E0000007","errorId":"oaeWzp-a2A0TCOm7D0FtnHcbg","errorCauses":[]}
//ErrorId and ErrorCode should be parsed from ErrorContent at the moment, but convenience methods will be added soon. OKTA-555564
}
The following features have been ported to 6.x:
- Iniline configuration, configuration via environment variables, appsettings.json or YAML files
- Manual pagination for collections
- Default retry strategy for 429 HTTP responses and ability to provide your own strategy
- Web proxy
- OAuth for Okta
We now use RestSharp as our internal REST API client library unlike previous versions which were using HttpClient
. For more details about other dependencies, please check out the Dependencies section here.
In order to provide better structuring, some endpoints have been moved from an existing client/API to their own API client:
Before:
var oktaClient = new OktaClient();
await oktaClient.Groups.AssignRoleAsync(groupId, new AssignRoleRequest
{
Type = RoleType.UserAdmin,
});
var roles = await oktaClient.Groups.ListGroupAssignedRoles(createdGroup.Id).ToListAsync();
/// ...
await oktaClient.Groups.AddGroupTargetToGroupAdministratorRoleForGroupAsync(createdGroup1.Id, role.Id, createdGroup2.Id);
var groups = await oktaClient.Groups.ListGroupTargetsForGroupRole(createdGroup1.Id, role.Id).ToListAsync();
Now:
var _roleAssignmentApi = new RoleAssignmentApi();
var role1 = await _roleAssignmentApi.AssignRoleToGroupAsync(createdGroup.Id, new AssignRoleRequest
{
Type = "SUPER_ADMIN"
});
var roles = await _roleAssignmentApi.ListGroupAssignedRoles(createdGroup.Id).ToListAsync();
/// ...
var _roleTargetApi = new RoleTargetApi();
await _roleTargetApi.AddGroupTargetToGroupAdministratorRoleForGroupAsync(group1.Id, role1.Id, group2.Id);
var groupTargetList = await _roleTargetApi.ListGroupTargetsForGroupRole(createdGroup1.Id, role1.Id).ToListAsync();
For more details about other APIs, please check out here.
The SDK uses Polly to implement the retry strategy when rate limit has been exceeded. The default retry strategy behavior and the way you configure it remains the same. However, if you want to provide your own retry logic you have to use Polly. Check out the README for more details.
In previous versions, null resource properties would result in a resource object with all its properties set to null
. Now, null resource properties will result in null
property value.
Before:
{ deserializedResource.Prop1.Should().Be("Hello World!");
prop1 : "Hello World!", => deserializedResource.NestedObject.Should().NotBeNull();
nestedObject: null deserializedResource.NestedObject.Prop1.Should().BeNull();
}
Now:
{ deserializedResource.Prop1.Should().Be("Hello World!");
prop1 : "Hello World!", => deserializedResource.NestedObject.Should().BeNull();
nestedObject: null
}
Since this is a breaking change in the default behavior, the major version of Okta.Sdk was incremented to 5; the latest version is now 5.0.0.
If you were relying on this behavior, make sure to update your code and verify the resource is not null
before accessing its properties.
The 3.x series of this library introduced a new client for Authorization Servers. This client had an issue when trying to retrieve policy rules for given Authorization Server Policy. In order to fix this issue, new policy models were created to represent both policies and policy rules for Authorization Servers.
Because this was a breaking change, Okta.Sdk was published with version numbers starting from 4.0.0.
All Authorization Server methods that manipulate Policies and/or Policy Rules have changed:
Below APIs has undergone a signature change.
public ICollectionClient<IPolicy> ListPolicies()
changed topublic ICollectionClient<IAuthorizationServerPolicy> ListPolicies()
Note that the method returns now an
IAuthorizationServerPolicy
.
public Task<IPolicy> CreatePolicyAsync(IPolicy policy, CancellationToken cancellationToken = default(CancellationToken))
changed topublic Task<IAuthorizationServerPolicy> CreatePolicyAsync(IAuthorizationServerPolicy policy, CancellationToken cancellationToken = default(CancellationToken))
Note that the method expects and returns now an
IAuthorizationServerPolicy
.
public Task<IPolicy> GetPolicyAsync(string policyId, CancellationToken cancellationToken = default(CancellationToken))
changed topublic Task<IAuthorizationServerPolicy> GetPolicyAsync(string policyId, CancellationToken cancellationToken = default(CancellationToken))
Note that the method returns now an
IAuthorizationServerPolicy
.
Below APIs has undergone a signature change.
public ICollectionClient<IPolicy> ListAuthorizationServerPolicies(string authServerId)
changed topublic ICollectionClient<IAuthorizationServerPolicy> ListAuthorizationServerPolicies(string authServerId)
Note that the method returns now an
ICollectionClient
ofIAuthorizationServerPolicy
.
public async Task<IPolicy> CreateAuthorizationServerPolicyAsync(IPolicy policy, string authServerId, CancellationToken cancellationToken = default(CancellationToken))
change topublic async Task<IAuthorizationServerPolicy> CreateAuthorizationServerPolicyAsync(IAuthorizationServerPolicy policy, string authServerId, CancellationToken cancellationToken = default(CancellationToken))
Note that the method expects and returns now an
IAuthorizationServerPolicy
.
public async Task<IPolicy> GetAuthorizationServerPolicyAsync(string authServerId, string policyId, CancellationToken cancellationToken = default(CancellationToken))
changed topublic async Task<IAuthorizationServerPolicy> GetAuthorizationServerPolicyAsync(string authServerId, string policyId, CancellationToken cancellationToken = default(CancellationToken))
Note that the method returns now an
IAuthorizationServerPolicy
.
public async Task<IPolicy> UpdateAuthorizationServerPolicyAsync(IPolicy policy, string authServerId, string policyId, CancellationToken cancellationToken = default(CancellationToken))
changed topublic async Task<IAuthorizationServerPolicy> UpdateAuthorizationServerPolicyAsync(IAuthorizationServerPolicy policy, string authServerId, string policyId, CancellationToken cancellationToken = default(CancellationToken))
Note that the method expects and returns now an
IAuthorizationServerPolicy
.
You now can get policy rules given an Authorization Server Policy:
var authorizationServerPolicy = await authorizationServer.GetPolicyAsync(policy.Id);
var authorizationServerPolicyRules = await authorizationServerPolicy.ListPolicyRules(authorizationServer.Id).ToListAsync();
Version 3.0.0 of this library introduces a number of breaking changes from previous versions; in addition to new classes some class definitions are no longer backward compatible due to method renames and signature changes, see Breaking Changes.
The following is a list of changes that break backward compatibility in version 3.0.0.
Okta.Sdk.OktaClient
CreatedScoped(Okta.Sdk.RequestContext requestContext)
— RenamedCreateScoped(Okta.Sdk.RequestContext requestContext)
Okta.Sdk.GroupsClient
ListGroups(string q, string filter, string after, int limit, string expand)
— Signature changedListGroups(string q, string filter, string after, int limit)
ListRules(int limit, string after, string expand)
— Renamed with new signatureListGroupRules(int limit, string after, string search, string expand)
CreateRuleAsync(Okta.Sdk.IGroupRule groupRule, CancellationToken cancellationToken)
— RenamedCreateGroupRuleAsync(Okta.Sdk.IGroupRule groupRule, CancellationToken cancellationToken)
DeleteRuleAsync(string ruleId, bool removeUsers, CancellationToken cancellationToken)
— Renamed with new signatureDeleteGroupRuleAsync(string ruleId, CancellationToken cancellationToken)
GetRuleAsync(string ruleId, string expand, CancellationToken cancellationToken)
— RenamedGetGroupRuleAsync(string ruleId, string expand, CancellationToken cancellationToken)
UpdateRuleAsync(Okta.Sdk.IGroupRule groupRule, string ruleId, CancellationToken cancellationToken)
— RenamedUpdateGroupRuleAsync(Okta.Sdk.IGroupRule groupRule, string ruleId, CancellationToken cancellationToken)
ActivateRuleAsync(string ruleId, CancellationToken cancellationToken)
— RenamedActivateGroupRuleAsync(string ruleId, CancellationToken cancellationToken)
DeactivateRuleAsync(string ruleId, CancellationToken cancellationToken)
— RenamedDeactivateGroupRuleAsync(string ruleId, CancellationToken cancellationToken)
GetGroupAsync(string groupId, string expand, CancellationToken cancellationToken)
— Signature changedGetGroupAsync(string groupId, CancellationToken cancellationToken)
ListGroupUsers(string groupId, string after, int limit, string managedBy)
— Signature changedListGroupUsers(string groupId, string after, int limit)
RemoveGroupUserAsync(string groupId, string userId, CancellationToken cancellationToken)
— RenamedRemoveUserFromGroupAsync(string groupId, string userId, CancellationToken cancellationToken)
Okta.Sdk.PoliciesClient
ListPolicies(string type, string status, string after, int limit, string expand)
— Signature changedListPolicies(string type, string status, string expand)
AddPolicyRuleAsync(Okta.Sdk.IPolicyRule policyRule, string policyId, bool activate, CancellationToken cancellationToken)
— Signature changedAddPolicyRuleAsync(Okta.Sdk.IPolicyRule policyRule, string policyId, CancellationToken cancellationToken)
Okta.Sdk.UserFactorsClient
AddFactorAsync(Okta.Sdk.IFactor factor, string userId, bool updatePhone, string templateId, int tokenLifetimeSeconds, bool activate, CancellationToken cancellationToken)
— Renamed with new signatureEnrollFactorAsync(Okta.Sdk.IUserFactor body, string userId, bool updatePhone, string templateId, int tokenLifetimeSeconds, bool activate, CancellationToken ca ncellationToken)
ActivateFactorAsync(Okta.Sdk.IVerifyFactorRequest verifyFactorRequest, string userId, string factorId, CancellationToken cancellationToken)
— Renamed with new signatureActivateFactorAsync(Okta.Sdk.IActivateFactorRequest body, string userId, string factorId, CancellationToken cancellationToken)
Okta.Sdk.UsersClient
ListUsers(string q, string after, int limit, string filter, string format, string search, string expand)
— Signature changedListUsers(string q, string after, int limit, string filter, string search, string sortBy, string sortOrder)
CreateUserAsync(Okta.Sdk.IUser user, bool activate, bool provider, Okta.Sdk.UserNextLogin nextLogin, CancellationToken cancellationToken)
— Signature changedCreateUserAsync(Okta.Sdk.ICreateUserRequest body, bool activate, bool provider, Okta.Sdk.UserNextLogin nextLogin, CancellationToken cancellationToken)
ListAppLinks(string userId, bool showAll)
— Signature changedListAppLinks(string userId)
ListUserGroups(string userId, string after, int limit)
— Signature changedListUserGroups(string userId)
ExpirePasswordAsync(string userId, bool tempPassword, CancellationToken cancellationToken)
— Signature changedExpirePasswordAsync(string userId, CancellationToken cancellationToken)
ResetAllFactorsAsync(string userId, CancellationToken cancellationToken)
— RenamedResetFactorsAsync(string userId, CancellationToken cancellationToken)
ResetPasswordAsync(string userId, Okta.Sdk.AuthenticationProviderType provider, bool sendEmail, CancellationToken cancellationToken)
— Removed; instead use any of the following:ForgotPasswordGenerateOneTimeTokenAsync(string userId, bool sendEmail, CancellationToken cancellationToken)
ForgotPasswordSetNewPasswordAsync(Okta.Sdk.IUserCredentials user, string userId, bool sendEmail, CancellationToken cancellationToken)
ExpirePasswordAsync(string userId, CancellationToken cancellationToken)
ExpirePasswordAndGetTemporaryPasswordAsync(string userId, CancellationToken cancellationToken)
ListAssignedRoles(string userId, string expand)
— RenamedListAssignedRolesForUser(string userId, string expand)
EndAllUserSessionsAsync(string userId, bool oauthTokens, CancellationToken cancellationToken)
— RenamedClearUserSessionsAsync(string userId, bool oauthTokens, CancellationToken cancellationToken)
The following is a list of context specific clients that are new in version 3.0.0. Instances of each are available as properties of an OktaClient instance where the name of the property is the name of the type with the "Client" suffix removed.
Okta.Sdk.AuthorizationServersClient
, see Authorization Servers API.Okta.Sdk.EventHooksClient
, see Event Hooks Management API.Okta.Sdk.FeaturesClient
, see Features API.Okta.Sdk.IdentityProvidersClient
, see Identity Providers API.Okta.Sdk.InlineHooksClient
, see Inline Hooks Management API.Okta.Sdk.LinkedObjectsClient
, see Linked Objects API.Okta.Sdk.TemplatesClient
, see Custom Templates API.Okta.Sdk.TrustedOriginsClient
, see Trusted Origins API.Okta.Sdk.UserTypesClient
, see User Types API.
The previous version of this library, Okta.Core.Client, has been rewritten from the ground up as Okta.Sdk (this project). This was done to improve stability and to add support for .NET Core alongside .NET Framework.
Because this was a breaking change, Okta.Sdk was published with version numbers starting from 1.0. The last published version of Okta.Core.Client is 0.3.3.
This library now supports a flexible configuration model that allows you to provide configuration in code, via a JSON or YAML file, or via environment variables.
The simplest way to construct a client is via code:
var client = new OktaClient(new OktaClientConfiguration
{
OrgUrl = "https://{{yourOktaDomain}}",
Token = "{{yourApiToken}}"
});
In version 0.3.3, you had to create a new UsersClient()
or call client.GetUsersClient()
to get access to methods that operated on a User (for example). This has now been simplified to client.Users
:
var vader = await client.Users.CreateUserAsync(...);
The Users
object acts as a collection, so you can also do:
var allUsers = await client.Users.ToArray();
The readme in this repository contains a number of usage examples.
In version 1.0 and above, every method that makes a network call is Task-returning and awaitable. Use await
when calling these methods, and avoid using .Result
or .Wait()
unless absolutely necessary.
In version 0.3.3, the AuthClient
class provided the ability to call the Authentication API to log a user in with a username and password, or perform other tasks like enrolling and challenging factors during authentication. The object and security model of the Authentication API compared the rest of the management APIs (Users, Factors, Groups, etc.) is different enough that it made sense to split it into two libraries.
Starting with version 1.0, Authentication has been broken out into a separate library, the Okta .NET Authentication SDK.
Many applications can use our ASP.NET and ASP.NET Core middleware to log users in without needing to call the Authentication API directly, and we recommend only using the Authentication SDK in complex scenarios that can't be handled with the middleware or widget.
If you have questions about this library or about the Okta APIs, post a question on our Developer Forum.
If you find a bug or have a feature request for this library specifically, post an issue here on GitHub.