Securing API integrations can be challenging, especially when dealing with sensitive eSignature workflows. This API security tutorial demonstrates how to implement robust OAuth 2.0 authentication for OneSpan Sign APIs, eliminating common security vulnerabilities in REST API integrations. You'll master JWT token authentication, learn the client credentials flow, and discover how OneSpan's authorization server enables secure bearer token authentication without exposing user credentials. Whether you're a developer new to OAuth or looking to enhance your existing OneSpan Sign integration, this guide provides practical code examples and API security best practices you can implement immediately.Introduction to OAuth 2.0 for developersOAuth 2.0 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service, such as APIs provided by Google, Microsoft, or OneSpan Sign. It’s widely adopted for securing APIs and delegating user access in web, desktop, and mobile applications.Unlike traditional authentication, OAuth 2.0 allows users to grant access to their data without sharing their credentials, using access tokens issued by a trusted authorization server.Key roles in OAuth 2.0Before diving into the flow, it’s important to understand the four main actors:RoleDescriptionResource ownerThe user who owns the data.ClientThe app requesting access to the user's data.Authorization serverIssues tokens to the client after authenticating the resource owner.Resource serverThe API or service that holds the user's data.OAuth 2.0 authorization code flowLet’s look at an example flow with OneSpan Sign: [Link]
Figure 1: Summary of OAuth Flow in OneSpan SignThe diagram in Figure 1 illustrates a server-to-server OAuth 2.0 flow using client credentials with JWT access tokens, specifically in the context of OneSpan Sign and an external authorization server for OneSpan Sign.Here is a step-by-step breakdown of the OAuth flow shown in the diagram:1. The application client (e.g., a backend service) initiates authentication by sending its client ID and client secret to the OneSpan Sign authorization server.2. The OneSpan Sign authorization server validates the client credentials.3. The application client includes the JWT access token in the authorization header (as bearer
) when calling OneSpan Sign REST API.4. When the OneSpan Sign API receives the request, it forwards the token to the OneSpan Sign authorization server (or a JWT validation service).5. The authorization server confirms whether the token is valid or not, and returns the result to OneSpan Sign.6. The OneSpan Sign API processes the request and returns a response to the application client.Why Use OAuth 2.0?Security: Users never share credentials with third-party apps.Scalability: Decouples authentication from resource access.Token-based: Enables fine-grained access control, token expiration, and revocation.Standardized: Works across different identity providers and APIs.Using cURLFirst, you need to get your OAuth credentials from your OneSpan Sign dashboard (i.e. client-id and client-secret). You will need to login as the administrator of the account and go to Admin > API Access.In the Authentication Settings, select the OAuth 2.0 option: [Link]
Figure 2: Authentication Settings in the OneSpan Sign dashboardNext, click on the OAuth 2.0 tab on the left-hand side. In here, you generate your client ID and client secret. Note the authorization server URL. [Link]
Figure 3: OAuth 2.0 in the OneSpan Sign dashboardNote that it can take up to 5 minutes for your OAuth client to be active.You’re now ready to make your first API call using OAuth. To retrieve an access token:cURL -H 'Content-Type: application/x-www-form-urlencoded' \-u "clientId:clientSecret" \https://sandbox.esignlive.com/oauth2/token \-d "grant_type=client_credentials"ParameterConditionDescriptionAuthorizationRequiredClient credentials are accepted in the form of Basic Auth pattern or a header parameter as per RFC 6749. In the request headers, pass the Base64 encoded string representing your 'client_id' and 'client_secret' values, appended to the text Basic as follows:``` Basic ```Base64 encoded client_id and client_secret = Base64 of "client_id:client_secret"grant_typeRequiredMust be set to client_credentials. Starting from release 24.R5, 'grant_type' will not be accepted as a query parameter. It will only be accepted as a form data.Successful responseA successful response looks like this:{ "access_token": "eyJraWQiOiIxOTk5OGJhOS1jYTY1LTQ3ODYtOGYzMi01ZGUxZDNhM2JhYTUiL....", "token_type": "Bearer", "expires_in": 299}ParameterDescriptionaccess_tokenThe requested access token. The app can use this token to authenticate to the secured resource, such as a web API.token_typeIndicates the token type value. The only type that the OneSpan Sign API supports is bearer.expires_inThe amount of time that an access token is valid (in seconds).Note: The OneSpan Sign API does not offer token refresh functionality, so a new token must be generated once the existing one expires. This requires additional handling on the application side by integrators using the OneSpan Sign API. On the other hand, the Java and .NET SDKs provides automatic token recreation on the client side, simplifying the process.Now, grab the access token and use it to make a call to the APIs:cURL -X POST --location 'https://http://sandbox.e-signlive.ca/api/packages' \ --header 'Content-Type: application/json' \ --header 'Authorization: Bearer yJraWQiOiIxOTk5OGJhOS1jYTY1LTQ3ODYtOGYzMi01ZGUxZDNhM2JhYTUiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJsWWZSMlluWThRaUVzaU1OT3Z5YmxuTWVMIiwiYXVkIjoibFlmUjJZblk4UWlFc2lNTk92eWJsbk1lTCIsIm5iZiI6MTcwNjI3MzI1MywiaXNzIjoiaHR0cDovL2ludGVybmFsLW9zcy1hdXRob3JpemF0aW9uLXNjc2lrYS0xMzE0NjM3MzA3LnVzLWVhc3QtMS5lbGIuYW1hem9uYXdzLmNvbSIsImV4cCI6MTcwNjI3MzU1MywiaWF0IjoxNzA2MjczMjUzLCJqdGkiOiJlNDZiYTViMi0zZmQzLTQzYjktOGQ0OS00Njg0NGEyYmQ1ZDIifQ.lGTWVxirs7G8nfXQC-enOpMUCdbmDp1hWnEHbLSuOKknPPrr0_EwkSMG2HHBGbMuDy_eY_jttLcdGI_PA10cikzCN0DSTnAmkAn7Av24BbqthTNqJsQfeCY0y8G14hnjmuDC7-yIsNO55X6YFtoF4MBxc7Z49PQUsjYmA9cxh0xgcMVLqRMbfHKdgIqSRLBtHdh4jdNn_xaoE-vCPCSnR2ocsacZEb-u0NX0f7Nt3f0l8g0JH2BR99RHUkUdc7T47rqda_wnhBB1fTTHt2p8mBkDbjMlaTkTAGX1U9tq0dBAePEDNw0DkDUVzGVNc2XafnBniiMZK0PvS4aN5PEkdw' \ --data '{ "name": "Dummy transaction", \ "description": "dummy transaction creation using OAuth", \ "type": "PACKAGE", \ "language": "en", \ "autocomplete": true, \ "sender": { \ "email": "[email protected]"\ }, \ "roles": [ \ { \ "id": "client", \ "type": "SIGNER", \ "index": 1, \ "signers": [ \ { \ "firstName": "Someone", \ "lastName": "Signer", \ "email": "[email protected]" \ } \ ], \ "name": "signer" \ }\ }, \ "name": "Sample Contract" \ }] \ }'Using Java SDKWhen using the Java or .NET SDK, the generated token is used until it expires instead of creating a new token on every call.The SDK handles this properly, by caching the clients created. Those clients retain the token and before every call made by the SDK, the token expiry is verified and the SDK will decide on whether a new token is needed or not.Hence, when using the SDK, there's no need to implement a check on the access token expiry since this is handled automatically.EslOAuthClientConfig config = new EslOAuthClientConfig.Builder() .withClientId("clientId") // Mandatory: replace with your clientId .withClientSecret("clientSecret") // Mandatory: replace with your clientSecret .withAuthenticationServer("authenticationServer") // Mandatory: replace with the OneSpan's authorization server url .withApiUrl("apiUrl") // Mandatory: replace with OneSpan's API url .withAllowAllSSLCertificatesFlag(allowAllSSLCertificatesFlag) // Optional: default false .withUseSystemProperties(useSystemProperties) // Optional: default false. If this is set to true then all proxy configurations should be passed as system properties .withProxyConfig(proxyConfig) // Optional: default null. To be passed if proxy is required and useSystemProperties is set to false .withHeaders(headers) // Optional: default empty map. To be passed if extra headers are need to the request .build(); EslClient eslClient = EslOAuthClientProvider.getInstance().getEslClient(config); //proceed to make calls to our API using eslClient.Using .NET SDKSimilarly to the Java SDK:OSSAuthClientConfig config = new OSSAuthClientConfig.Builder() .WithClientId(clientId) // Mandatory: replace with your clientId .WithClientSecret(clientSecret) // Mandatory: replace with your clientSecret .WithAuthenticationServer(authenticationServer)// Mandatory: replace with the OneSpan's authorization server url .WithApiUrl(apiUrl) // Mandatory: replace with OneSpan's API url .WithAllowAllSSLCertificatesFlag(allowAllSSLCertificatesFlag) // Optional: default false .WithUseSystemProperties(useSystemProperties) // Optional: default false. If this is set to true then all proxy configurations should be passed as system properties .WithProxyConfiguration(proxyConfig) // Optional: default null. To be passed if proxy is required and useSystemProperties is set to false .WithHeaders(headers) // Optional: default empty map. To be passed if extra headers are need to the request .Build(); OssClient ossClient = OSSAuthClientProvider.Instance.GetOssClient(config); //proceed to make calls to our API using ossClientIn both cases above the ossClient / eslClient will be cached in memory and the SDKs will enforce that the clients reuse the oAuth2 token and regenerate a new token when it expires. Join the OneSpan Community You’ll be able to ask questions on our forum, download sample code files, and stay up to date on OneSpan news.
Join now