This SDK allows you to enable any Android application to easily support Kerberos based Single Sign-On (SSO) using any Android Kerberos Authenticator such as the Hypergate Authenticator. It handles SPNEGO negotiate tokens for both native HTTP requests and WebViews, and works with all major MDM/EMM/UEM platforms (supported EMMs).
Learn more at hypergate.com. For a full integration walkthrough, including how this fits into the NTLM deprecation, read our blog post: Moving Your Android App off NTLM: Kerberos SSO with the Hypergate SDK.
To include the Hypergate SDK into your project, simply add the following to your app build.gradle:
....
dependencies {
....
implementation "com.hypergate:sdk:1.6.0"
....
}
....If your application does not have its own app restrictions, just add the following line to your AndroidManifest.xml:
....
<application>
....
<meta-data
android:name="android.content.APP_RESTRICTIONS"
android:resource="@xml/hypergate_sdk_restrictions" />
....
</application>
....Otherwise copy the restrictions from the resource file (hypergate_sdk_restrictions.xml) into your own restrictions file.
There are two ways to use this sdk, which depends on how you plan to consume your kerberized service:
- You have a hybrid mobile app (i.e. Cordova, Capacitor, etc) or simply use a WebView to consume you service. See WebView Request below
- You want to perform the native service requests yourself. See Native Token Request below.
This is very easy because you don't have to do anything other than include the library as shown in the Getting Started section. The magic that happens in the background is that your AndroidManifest.xml will receive the needed Restrictions (hypergate_sdk_restrictions.xml) to transparently enable every WebView in your project.
After you included this plugin, your application will receive new managed configurations:
- Account type for HTTP Negotiate authentication: this controls which account type your webview will be looking for whenever there is an authentication challenge. This should be set to 'ch.papers.hypergate'
- Authentication server allowlist: this controls which servers are allowed to request authentication tokens from hypergate. This can be either a wildcard (*) or the domains you want to enable
- Authentication server whitelist (deprecated): use "Authentication server allowlist" instead
- Whether NTLMv2 authentication is enabled: the title is self explanatory and actually has nothing to do with Hypergate.
After you configured the options in your MDM/EMM/UEM (thing that pushes restrictions aka managed configurations) the webview will deal with all ajax and native requests on its own transparently from your app. This means all your requests will be authenticated automagically.
Note: If you decide not to use the standard ajax request and have a native plugin to perform requests, you will need to go with the "Native Token Request" below.
There are two ways to request tokens either synchronously, which will block until it managed to get the token or asynchronously, which will return you the token in the success callback you specified.
Every request method also comes in a "silently" variant (e.g. requestTokenSilentlySync). The
regular variants take an Activity and may show UI (for example an account picker or a consent
screen) if user interaction is required. The silent variants take a plain Context, never show
any UI and fail instead, which makes them suitable for background work such as sync adapters,
workers or push handlers.
val token = Hypergate.requestTokenSync(activity, "HTTP@myhost.com")
// or from a background component, without any UI:
val token = Hypergate.requestTokenSilentlySync(context, "HTTP@myhost.com")final String token = Hypergate.Companion.requestTokenSync(activity, "HTTP@myhost.com");
// or from a background component, without any UI:
final String token = Hypergate.Companion.requestTokenSilentlySync(context, "HTTP@myhost.com");Hypergate.requestTokenAsync(activity, "HTTP@myhost.com",
{ negotiateToken -> Log.d("TOKEN", negotiateToken) },
{ exception -> Log.d("ERROR", exception.message) })
// or from a background component, without any UI:
Hypergate.requestTokenSilentlyAsync(context, "HTTP@myhost.com",
{ negotiateToken -> Log.d("TOKEN", negotiateToken) },
{ exception -> Log.d("ERROR", exception.message) })Hypergate.Companion.requestTokenAsync(activity, "HTTP@myhost.com",
new Function1<String, Unit>() {
@Override
public Unit invoke(String negotiateToken) {
Log.d("TOKEN", negotiateToken);
return Unit.INSTANCE;
}
},
new Function1<Exception, Unit>() {
@Override
public Unit invoke(Exception exception) {
Log.d("ERROR", exception.getMessage());
return Unit.INSTANCE;
}
}
);The requestToken* methods return only the negotiate token string. If you need the full
AccountManager result, use the requestTokenBundle* counterparts, which return the raw result
Bundle (the token itself is stored under AccountManager.KEY_AUTHTOKEN).
For multi-round-trip SPNEGO context establishment, the bundle variants additionally accept:
incomingAuthToken: the token received from the server in the previous round tripspnegoContext: an opaque context identifier returned in the previous result bundle, used to retain state between round trips
val bundle = Hypergate.requestTokenBundleSync(
activity, "HTTP@myhost.com", incomingAuthToken, spnegoContext
)
val token = bundle.getString(AccountManager.KEY_AUTHTOKEN, "")The SDK also exposes a few helpers around the Hypergate account:
// true if a Hypergate account is present on the device
Hypergate.hasAccount(context)
// all Hypergate accounts (the account type honors the managed configuration)
val accounts: Array<Account> = Hypergate.getAccounts(context)
// system intent to let the user pick / add a Hypergate account
startActivity(Hypergate.getAccountChooserIntent(context))The following examples showcase how to authenticate HTTP requests using Hypergate for the most common HTTP Request libraries used on the Android platform. In a nutshell the only thing we do is request a token and put it into the request headers. As of now we cover the requests using Volley and OkHttp, if there is another HTTP Library you would like us to showcase feel free to reach out.
val queue = Volley.newRequestQueue(activityRule.activity)
val url = "https://securedendpoint.com"
val stringRequest = object : StringRequest(
Method.GET, url,
Response.Listener<String> { response ->
},
Response.ErrorListener { Log.d("ERROR", "That didn't work!") }) {
override fun getHeaders(): MutableMap<String, String> {
val headers = super.getHeaders()
val token = Hypergate.requestTokenSync(
activityRule.activity,
"HTTP/${Uri.parse(this.url).host}"
)
headers.put("Authorization", "Negotiate ${token}")
return headers
}
}
queue.add(stringRequest)Simply create a interceptor:
internal class HypergateOkHttpInterceptor(private val activity: Activity) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val token = Hypergate.requestTokenSync(
activity,
"HTTP/${request.url.host}"
)
val authenticatedRequest = request.newBuilder()
.addHeader("Authorization", "Negotiate ${token}")
.build()
return chain.proceed(authenticatedRequest)
}
}And then use it in your code:
val client = OkHttpClient.Builder()
.addInterceptor(HypergateOkHttpInterceptor(activityRule.activity))
.build()
val request = Request.Builder()
.url("https://securedendpoint.com")
.build()
val response = client.newCall(request).execute()If you are using Cordova for your hybrid applications, you can easily enable your project to support SSO by simply installing a plugin. You can read more about it here:
All exception callbacks return a HypergateException object. This object has no logic but holds 2 properties:
- Code: This code is used by the app to switch-case and identify the error.
- Message: A verbose description (in English) of the actual error.
The following table includes a list of all errors you can encounter:
| Code | Message | Description |
|---|---|---|
| 101 | no accounts found | No accounts found, make sure you added your package name to the hypergate discoverability list |
This library is licensed under MIT. Feel free to provide pull requests, we'll be happy to review and include them. If you find any bugs, submit an issue or open pull-request, helping us catch and fix them.
$ git clone https://github.com/hypergate-com/hypergate-sdk
$ cd hypergate-sdk
$ ./gradlew clean buildSince the Hypergate uses various android platform features, all the provided tests are Android instrumentation tests (androidTest). Other projects would ideally test the non-Android components with plain JUnit tests (test).
$ ./gradlew connectedAndroidTest # to run the Android instrumentation testsThe full javadoc documentation can be generated with:
$ ./gradlew :sdk:javaDocReleaseGeneration
$ open sdk/build/intermediates/java_doc_dir/release/javaDocReleaseGeneration/index.htmlA javadoc jar is also produced as part of every release and published alongside the library.
In case you require support feel free to reach out to us (support@hypergate.com) or via hypergate.com/get-in-touch.