.NET IOM

A .NET version of IOM.dll can be found in the \Utilities\Aras Innovator <Version> IOM SDK\.NET directory on the CD Image. You can copy this to another location and reference it using .NET project. IOM APIs belong to Aras.IOM namespace. First, every application must create a connection with the Aras Innovator server and login to the Aras Innovator server using the connection. If the login succeeds, then an instance of the Innovator class must be created with the connection.

Here is a small sample:

using Aras.IOM;

String url = "https://myserver/MyInnovator/Server/InnovatorServer.aspx";

// database, username and password values should be entered by user

String db;

String userName;

String password;

HttpServerConnection conn =

IomFactory.CreateHttpServerConnection( url, db, userName, password );

Item login_result = conn.Login();

if( login_result.isError() )

throw new Exception("Login failed");

Innovator inn = IomFactory.CreateInnovator( conn );

Note: You can pass either encrypted (if required, use static method Innovator.ScalcMD5(string pwd) for encryption) or non-encrypted password (as shown in the previous example) to the method IomFactory.CreateHttpServerConnection(…). If you pass a non-encrypted password, it’ll be encrypted inside the method. Otherwise, the password is passed to the server without modifications.

It’s highly recommended to logout of Aras Innovator prior to exiting the application:

conn.Logout();

Obtaining an Access Token

The IOM API for authentication supports multiple authentication grants to obtain access token: a Resource Owner Password Credentials grant (further on Password grant), an Authorization Code grant and our custom Impersonate grant (a custom grant based on Client Credentials grant). You can use an obtained access token in subsequent requests to the Aras Innovator server.

Note: See more about OAuth 2.0 authorization grants in the OAuth 2.0 RFC specification: https://tools.ietf.org/html/rfc6749#section-4. Refer to the Impersonate grant chapter for more information.

The IOM API for authentication includes a public ITokenProvider interface and its default implementations: PasswordTokenProvider, ImpersonateTokenProvider, AuthorizationFlowTokenProvider and WindowsTokenProvider. If the default implementation does not cover your needs, it is possible to provide a custom implementation of the ITokenProvider.

Warning: Before creating a token provider, it is necessary to get a DiscoveryDocument that contains information about OAuth server configuration.

Discovery of OAuth Server

The Discovery mechanism allows clients to find the OAuth server and request its configuration.

The IOM OAuth API includes a public IDiscoveryDocumentProvider interface and its default implementation of the DiscoveryDocumentProvider.

DiscoveryDocumentProvider does the following:

  1. Sends a request to http://<company.net>/<Innovator>/Server/OAuthServerDiscovery.aspx to determine the OAuth server location. OAuth server URLs are specified in InnovatorServerConfig.xml and look like the following:

    <OAuthServerDiscovery>
    <Urls>     <Url value="http://company.net/Innovator/OAuthServer/" />     
    <!-- Url value="http://company.local/Innovator/OAuthServer/" /-->
    </Urls>
    </OAuthServerDiscovery>

  1. Requests the OAuth server discovery configuration.

The DiscoveryDocumentProvider receives all OAuth server URLs that are configured in InnovatorServerConfig.xml and prioritizes the list of OAuth server URLs according to the following order:

  1. Last accessible OAuth server URL if presented.

  2. URLs with the same host as the Aras Innovator server host.

  3. All other URLs received from OAuthServerDiscovery.aspx.

After receiving the OAuth server URLs, the DiscoveryDocumentProvider tries to find an accessible path by requesting /.well-known/openid-configuration paths from the  list according to priority. After an accessible path is found, DiscoveryDocumentProvider creates an instance of DiscoveryDocument based on the response from the OAuth server and saves the last accessible URL.

The DiscoveryDocument contains information about:

  • OAuth server endpoints (e.g., authorization and token endpoints),

  • Protocol type (standard or custom),

  • Protocol version.

This information is used while creating token providers in cases where it is necessary to provide the DiscoveryDocument by itself or some data from it.

The following example shows how to use the DiscoveryDocumentProvider:

var discoveryDocumentProvider = new DiscoveryDocumentProvider(

"https://myserver/MyInnovator/Server/InnovatorServer.aspx");

 

DiscoveryDocument discoveryDocument =

await discoveryDocumentProvider.GetDiscoveryDocumentAsync();

 

Note: The GetDiscoveryDocumentAsync method can take CancellationToken as a parameter to provide the possibility of canceling a task.
The default DiscoveryDocumentProvider implementation does not use a cache for DiscoverDocument. If caching is necessary for your application, it is possible to implement a wrapper with the cache.

The DiscoveryDocument instance has the following properties:

  • Issuer – token issuer identifier (OAuth server).

  • JwksUriJSON Web Key Set document URI.

  • AuthorizeEndpoint – URL of authorization endpoint.

  • TokenEndpoint – URL to token endpoint.

  • EndSessionEndpoint – URL of end session endpoint.

  • ProtocolVersion – version of protocol that is used by the OAuth server.

  • ProtocolInfo – information about protocol that is used by the OAuth server.

  • ProtocolType – type of protocol that is used by the OAuth server.

  • KeySetJson – JSON web key set.

A Protocol is used to determine what authentication header is used by the OAuth server. The Standard protocol type uses the "Authorization" header and 401 HTTP status code for "Unauthorized", the Custom protocol type uses "X-Aras-Authorization" and 498 HTTP status code for "Unauthorized". The Custom protocol type is necessary in case the Standard protocol type conflicts with other protection mechanisms (e.g., when all server resources are protected by Windows authentication).

The DiscoveryDocument class also makes it possible to get any other property from a discovery document with the following methods:

  • TryGetString(string name) – gets a string value from the discovery document by name. If the property is not found, it returns null.

  • TryGetBoolean(string name) – gets a  boolean value from the discovery document by name. If the property is not found or is not of the boolean type, it returns false.

  • TryGetStringArray(string name) – gets a string array value from the discovery document by name. If the property is not found or is not of the string array type, it returns null.

Note: For more information about discovery see https://openid.net/specs/openid-connect-discovery-1_0.html.

Password Grant

The Password grant is useful in cases where the client can obtain the user credentials. Password grants are often used for legacy or migration reasons. It is recommended to use other grant types whenever possible.

Note: See more about Password grants in the OAuth 2.0 RFC specification: https://tools.ietf.org/html/rfc6749#section-4.3

You must use the PasswordTokenProvider to get an access token using a Password grant... You must pass PasswordTokenProviderOptions to create the PasswordTokenProvider:

// database, username and password values should be entered by user

String db;

String userName;

String password;

var tokenProvider = new PasswordTokenProvider(

    new PasswordTokenProviderOptions()

    {

        TokenEndpoint = discoveryDocument.TokenEndpoint,

        ClientId = "IOMApp",

        Scope = "Innovator",

        Database = db,

        UserName = userName,

        Password = password

    });

PasswordTokenProviderOptions use the following options:

  • TokenEndpoint – URL of OAuth server token endpoint.

  • ClientId – ID of OAuth client that requests token.

  • Scope – scope that will be used to request token. Currently all Aras Innovator resources require Innovator scope.

  • Database – name of database to login to.

  • UserName – user name.

  • Password – user password.

Note: A password can be passed either as plain text or MD5/SHA256 hash (depending on FIPS mode).

To get an access token it is necessary to call the GetAccesTokenAsync method. This method sends a request to the OAuth server token endpoint and returns the access token created by a Password grant.

string accessToken = await tokenProvider.GetAccessTokenAsync();

 

Note: The GetAccessTokenAsync method can take CancellationToken as a parameter to provide the possibility of cancelling a task.

Impersonate Grant

The Client Credentials grant is usually used for communication between servers. Communication between Aras Innovator servers requires user information. The token requested by the Client Credentials grant does not contain user information, so the Impersonate grant has been implemented.

Note: Learn more about the Client Credentials grant in the OAuth 2.0 RFC specification: https://tools.ietf.org/html/rfc6749#section-4.4.

The Impersonate grant is a custom grant like the Client Credentials grant. The Impersonate grant is designed to authenticate client by client assertion tokens and provide access tokens by username and database. To request an access token using the Impersonate grant it is necessary to send the client assertion token, scope, database, and username to the token endpoint of the OAuth server.

  1. Before you can use the Impersonate Grant with custom applications, you must do the following: Use OpenSSL to generate the certificates pair (PFX, CER files).

  1. Put the public certificate (CER file) in the {Installation_Dir}\OAuthServer\App_Data\Certificates\ directory.

  2. Use a private certificate (PFX file) in the custom application.

  3. Configure the new OAuth client based on the InnovatorServer clientRegistry node using the allowed impersonate grant located in the {Installation_Dir}\OAuthServer\OAuth.config directory.

Use the configured OAuth client for authentication with the Impersonate grant.

Note: See more information about assertion tokens in JWT specification: https://tools.ietf.org/html/rfc7523

Warning: The Impersonate grant is used by the trusted server application.

IClientAssertionProvider is used to provide an assertion token for the ImpersonateTokenProvider. The default implementation of the IClientAssertionProvider is JwtBearerClientAssertionProvider. To create the JwtBearerClientAssertionProvider it is necessary to provide JwtBearerClientAssertionProviderOptions.

var certificate = new X509Certificate2(

privateCertificateFilePath,

certificatePassword,

keyStorageFlags);

var assertionProvider = new JwtBearerClientAssertionProvider(

new JwtBearerClientAssertionProviderOptions

{

        ClientId = "ClientId",

        Audience = discoveryDocument.Issuer,

        Certificate = certificate,

        AssertionTokenLifetime = TimeSpan.FromSeconds(300)

});

Note: You can load the Certificate in X509Certificate2 from Certificate Store. For more information see the other contructors of X509Certificate2 https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2.-ctor

JwtBearerClientAssertionProviderOptions have the following options:

  • ClientId – the OAuth client ID that requests the token.

  • Audience – determines where the assertion token will be used. In the case of the Impersonate grant, the audience is the OAuth server. To get the correct value use the Issuer property of DiscoveryDocument.

  • Certificate – the X509Certificate2 instance of the client private certificate.

  • AssertionTokenLifeTime – lifetime of the assertion token. The default value is 300 seconds.

To get the client assertion you must call the GetClientAssertionAsync method:

ClientAssertion assertion =

await assertionProvider.GetClientAssertionAsync();

Client assertion contains the following properties:

  • Type – assertion token type.

  • Value – assertion token.

To create the ImpersonateTokenProvider it is necessary to pass ImpersonateTokenProviderOptions:

// database and username values should be entered by user

String db;

String userName;

var tokenProvider = new ImpersonateTokenProvider(

    new ImpersonateTokenProviderOptions()

    {

        TokenEndpoint = discoveryDocument.TokenEndpoint,

        ClientAssertionProvider = assertionProvider,

        Scope = "Innovator",

Database = db,

        UserName = userName

    });

ImpersonateTokenProviderOptions uses the following options:

  • TokenEndpoint – URL of OAuth server token endpoint.

  • ClientAssertionProvider – provider of assertion token.

  • Scope – scope used to request the token.

  • Database – name of the database to login to.

  • UserName – user name.

 

Note: Custom IClientAssertionProvider implementation can be provided. There is no need to provide a password because authentication is performed for client-by-client credentials. In this case client credentials are assertion tokens.

You must call the GetAccessTokenAsync method to get an access token. This method sends a request to the OAuth server token endpoint and returns the access token created by the Impersonate grant.

string accessToken = await tokenProvider.GetAccessTokenAsync();

 

Note: The  GetAccessTokenAsync method can take the CancellationToken as a parameter for cancelling a task.

Authorization Code Grant

The Authorization Code grant type is used to obtain access tokens and is optimized for confidential clients. Since this is a redirection-based flow, the client must be capable of interacting with the resource owner's user-agent (typically a web browser) and capable of receiving incoming requests (via redirection) from the authorization server.

Note: See more about the Authorization Code grant in the OAuth 2.0 RFC specification: https://tools.ietf.org/html/rfc6749#section-4.1

To request an access token using an authorization code grant it is necessary to use the AuthorizationFlowTokenProvider. To create the AuthorizationFlowTokenProvider it is necessary to create an INavigator instance.

The default implementation of INavigator is WpfBrowserNavigator which can be found in Aras.IOM.OAuth.WpfBrowserNavigator nuget  package. This navigator is used to allow browser-based login from WPF and WinForms applications. WpfBrowserNavigator uses WebView2 control to process navigation to URLs.

Aras.IOM.OAuth.WpfBrowserNavigator package located at \Utilities\Aras Innovator <Version> IOM SDK\Packages directory on the CD Image.

Microsoft Edge WebView2 Runtime is a required component for WebView2 control. WebView2 Runtime must be installed in each machine where developed application is run. It can be downloaded by following link: https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section

To use Aras.IOM.OAuth.WpfBrowserNavigator*.nupkg, it should be placed into the NuGet feed. See Setting up local NuGet feed to find information how to create local NuGet feed.

Note: The Aras.IOM.OAuth.WpfBrowserNavigator package has dependency to Microsoft.Web.WebView2 package. If your project is targeted to net472 and has SDK project type or you use PackageReference to refer to packages in non SDK project type than you need additionally install Microsoft.Web.WebView2 package via Manager of NuGet packages. It is issue of MSBuild and more information is there:

Note: https://github.com/dotnet/sdk/issues/20073

Note: https://github.com/dotnet/sdk/issues/3031

To create the WpfBrowserNavigator it is necessary to call a parameterless constructor. It is possible to define values for displaying a browser window:

var navigator = new WpfBrowserNavigator

{

    Width = 900,

    Height = 600,

    Title = "Innovator"

};

WpfBrowserNavigator has the following properties:

  • Width – width of browser window. Default value is 900.

  • Height – height of browser window. Default value is 600.

  • Title – title of browser window. Default value is "Innovator".

  • Owner – owner of browser window in case of using WpfBrowserNavigator in WPF application. The browser window will be centered within the owner window. If this property is not set, the browser window will be centered within the current screen working area.

  • OwnerHandle – owner of browser window in case of using WpfBrowserNavigator in not WPF application. The browser window will be centered within the owner window. If this property is not set, the browser window will be centered within the current screen working area.

Note: The IOM.OAuth.WpfBrowserNavigator.dll is stored next to .NET version of IOM.dll.

To create the AuthorizationFlowTokenProvider it is necessary to pass AuthorizationFlowTokenProviderOptions:

// database value should be entered by user

String db;

var tokenProvider = new AuthorizationFlowTokenProvider(

    new AuthorizationFlowTokenProviderOptions()

    {

DiscoveryDocument = discoveryDocument,

        ClientId = "IOMApp",

        RedirectUrl = "iomapp://token",

        Scope = "Innovator",

        Database = db,

        AuthenticationType = "Windows",

        ResponseMode = ResponseMode.Query,

        Navigator = navigator

});

AuthorizationFlowTokenProviderOptions have the following options:

  • DiscoveryDocument – instance of DiscoveryDocument.

  • GrantType – a grant type that is used to request an access token. The default value is AuthorizationCode. Only the AuthorizationCode grant is currently supported.

  • ClientId – the ID of the OAuth server client that requests a token.

  • RedirectUrl – URL to return a token.

  • Scope – scope used to request a token.

  • Database – name of the database to login to.

  • AuthenticationType – authentication type to use to authenticate.

  • ResponseMode – response mode. The Default value is Query.

  • Navigator – instance of INavigator.

Note: Database and AuthenticationType options are implemented to allow direct login. If both values are set and the required authentication type allows login without the Aras Innovator login page, the OAuth server will try to redirect to the specified authentication type automatically and log the external user into the specified database.

You must call the GetAccessTokenAsync method to get an access token. This method sends a request to the OAuth server Authorize endpoint, receives an authentorization code, sends the authorization code to the token endpoint and gets the access token issued by the Authorization Code grant.

string accessToken = await tokenProvider.GetAccessTokenAsync();

Note: The GetAccessTokenAsync method can take the CancellationToken as a parameter to provide the possibility of cancelling a task.

Authorization Code Grant with Windows Identity Provider

It is possible to use the IOM API for login via Windows authentication without user interaction. To request an access token using Windows authentication you must use the WindowsTokenProvider. This provider uses an Authorization Code grant.    

To create a WindowsTokenProvider it is necessary to provide WindowsTokenProviderOptions:

// database value should be entered by user

String db;

var tokenProvider = new WindowsTokenProvider(

    new WindowsTokenProviderOptions()

    {

        ClientId = "IOMApp",

        DiscoveryDocument = discoveryDocument,

        RedirectUrl = "iomapp://token",

        Scope = "Innovator",

        AuthenticationType = "Windows",

        Database = db

    });

WindowsTokenProviderOptions have the following options:

  • DiscoveryDocument – an instance of DiscoveryDocument.

  • ClientId – ID of OAuth server client that requests the token.

  • RedirectUrl – URL to return the token.

  • Scope – scope used to request the token.

  • Database – name of the database to login to.

  • AuthenticationType – the name of the authentication type used for Windows authentication in the OAuth server.

  • MaxRedirectsCount – maximum number of redirects during authentication. This option is required to limit the number of redirects in a redirection-based flow to prevent eternal redirections. Default value is 10.

Note: The "iomapp://token" is the default RedirectUri value for the IOMApp client of the OAuth server.

You must call the GetAccessTokenAsync method to get an access token. This method sends a request to the OAuth server Authorize endpoint, receives the authorization code, sends the authorization code to the token endpoint, and gets an access token issued by the Authorization Code grant.

string accessToken = await tokenProvider.GetAccessTokenAsync();

Note: The GetAccessTokenAsync method can take CancellationToken as a parameter to nake it possible to cancel a task.

Using token providers with OData requests

To make OData requests valid for the Aras Innovator server it is necessary to have an access token. The access token should be presented in the "Authorization" header in the case of the Standard protocol type or in "X-Aras-Authorization" in the case of the Custom protocol type. The following example demonstrates using the token provider for creating an OData request to the Aras Innovator server:

// database, username and password values should be entered by user

String db;

String userName;

String password;

var discoveryDocumentProvider = new DiscoveryDocumentProvider(

"https://myserver/MyInnovator/Server/InnovatorServer.aspx");

var discoveryDocument =

await discoveryDocumentProvider.GetDiscoveryDocumentAsync();

var tokenProvider = new PasswordTokenProvider(

    new PasswordTokenProviderOptions()

    {

        TokenEndpoint = discoveryDocument.TokenEndpoint,

        ClientId = "IOMApp",

        Scope = "Innovator",

        Database = db,

        UserName = userName,

        Password = password

    });

string token = await tokenProvider.GetAccessTokenAsync();

WebRequest request =

WebRequest("https://myserver/MyInnovator/Server/odata/" + resourcePath);

request.Method = "GET";

request.Headers.Add(

discoveryDocument.ProtocolInfo.AuthorizationHeader, "Bearer " + token));

WebResponse response = request.GetResponse();

Using Token Provider with HttpServerConnection

The IomFactory class has a public method for creating a connection to the Aras Innovator server based on the provided ITokenProvider implementation (from 12.0):

public static HttpServerConnection CreateHttpServerConnection(

string innovatorServerUrl,

ITokenProvider tokenProvider,

ProtocolType protocolType);

Note: There is CreateHttpSeverConnection overload that requires a database and does not require a ProtocolType and uses the Standart ProtocolType by default. It is not recommended to use this overload because this method is obsolete. It is recommended to use DiscoveryDocument to get the current ProtocolType and pass it to the CreateHttpSeverConnection method.

There are also public methods for creating the connection without providing the ITokenProvider. They use default ITokenProvider implementations: CreateHttpServerConnection uses PasswordTokenProvider, CreateWinAuthHttpServerConnection uses WindowsTokenProvider.

Note: Connections that are created by CreateHttpServerConnection and CreateWinAuthHttpServerConnection methods without providing ITokenProvider also support legacy authentication for backward compatibility with Aras Innovator 11 SP14 and earlier.

Here is the example of code using the CreateHttpServerConnection method with PasswordTokenProvider:

// database, username and password values should be entered by user

String db;

String userName;

String password;

var discoveryDocumentProvider = new DiscoveryDocumentProvider(

"https://myserver/MyInnovator/Server/InnovatorServer.aspx");

var discoveryDocument =

await discoveryDocumentProvider.GetDiscoveryDocumentAsync();

var tokenProvider = new PasswordTokenProvider(

    new PasswordTokenProviderOptions()

    {

        TokenEndpoint = discoveryDocument.TokenEndpoint,

        ClientId = "IOMApp",

        Scope = "Innovator",

        Database = db,

        UserName = userName,

        Password = password

    });

HttpServerConnection httpServerConnection = CreateHttpServerConnection(

"https://myserver/MyInnovator/Server/InnovatorServer.aspx",

tokenProvider,

discoveryDocument.ProtocolType);

httpServerConnection.Login();

Here is an example of code using the CreateHttpServerConnection method with AuthorizationFlowTokenProvider:

// database value should be entered by user

String db;

var discoveryDocumentProvider = new DiscoveryDocumentProvider(

"https://myserver/MyInnovator/Server/InnovatorServer.aspx");

var discoveryDocument =

await discoveryDocumentProvider.GetDiscoveryDocumentAsync();

var navigator = new WpfBrowserNavigator();

var tokenProvider = new AuthorizationFlowTokenProvider(

    new AuthorizationFlowTokenProviderOptions()

    {

DiscoveryDocument = discoveryDocument,

        ClientId = "IOMApp",

        RedirectUrl = "iomapp://token",

        Scope = "Innovator",

        Database = db,

        AuthenticationType = "Windows",

        ResponseMode = ResponseMode.Query,

        Navigator = navigator

});

HttpServerConnection httpServerConnection = CreateHttpServerConnection(

"https://myserver/MyInnovator/Server/InnovatorServer.aspx",

tokenProvider,

discoveryDocument.ProtocolType);

httpServerConnection.Login();

Note: It is recommended to set values that are necessary for authentication from config files or user input so changing OAuth client settings does not affect your application.