|
| 1 | +# PlusAuth OpenID Connect Library for Android |
| 2 | +[](https://github.com/PlusAuth/plusauth-oidc-android/blob/master/LICENSE)   [](https://github.com/PlusAuth/plusauth-oidc-android/issues)  |
| 3 | + |
| 4 | +Android OpenId Connect library that can be used with any OIDC provider for easy to use authentication. We value developer time and effort, so we developed a dead simple zero config library that any one could easily integrate in a minute, while allowing veterans of authentication to customize everything to their needs. |
| 5 | + |
| 6 | +## Table of Contents |
| 7 | + |
| 8 | +- [Requirements](#Requirements) |
| 9 | +- [Installation](#Installation) |
| 10 | +- [OIDC Provider Configuration](#OIDC-provider-configuration) |
| 11 | +- [Configuration](#Configuration) |
| 12 | +- [Login](#Login) |
| 13 | +- [Logout](#Logout) |
| 14 | +- [Using the Tokens](#Using-the-Tokens) |
| 15 | + - [Get User Info](#Get-user-info) |
| 16 | + - [Exchange Auth Token for Credentials](#Exchange-auth-token-for-credentials) |
| 17 | + - [Renew Credential with a Refresh Token](#Renew-credential-with-a-refresh-token) |
| 18 | + - [Revoke a Token](#Revoke-a-Token) |
| 19 | +- [Advanced Usage](#Advanced-usage) |
| 20 | + - [Storage](#Storage) |
| 21 | + - [Encryption](#Encryption) |
| 22 | +- [Example App](#Example-app) |
| 23 | +- [Acknowledgements](#Acknowledgements) |
| 24 | +- [License](#License) |
| 25 | + |
| 26 | + |
| 27 | + |
| 28 | +## Requirements |
| 29 | + |
| 30 | +PlusAuth OpenID Connect Library for Android supports Android Apis starting from 16(Jelly Bean) and above. |
| 31 | + |
| 32 | +## Installation |
| 33 | + |
| 34 | +Add the library dependency to your `build.gradle` file: |
| 35 | + |
| 36 | +```gradle |
| 37 | +implementation 'com.plusauth.android:oidc:1.0.0' |
| 38 | +``` |
| 39 | + |
| 40 | +This library uses and requires Java 8 support. Check out [Android Documentation](https://developer.android.com/studio/write/java8-support.html) to learn more. |
| 41 | + |
| 42 | +To enable, add the following following to your `build.gradle` file |
| 43 | + |
| 44 | +```gradle |
| 45 | +android { |
| 46 | + ... |
| 47 | + ... |
| 48 | + compileOptions { |
| 49 | + sourceCompatibility JavaVersion.VERSION_1_8 |
| 50 | + targetCompatibility JavaVersion.VERSION_1_8 |
| 51 | + } |
| 52 | + // For Kotlin projects |
| 53 | + kotlinOptions { |
| 54 | + jvmTarget = "1.8" |
| 55 | + } |
| 56 | +} |
| 57 | +``` |
| 58 | +### OIDC Provider Configuration |
| 59 | + |
| 60 | + |
| 61 | +To use the library you must have a OIDC provider account. In this part we will use PlusAuth as an example. |
| 62 | + |
| 63 | + |
| 64 | +1. Create a Plusauth account and a tenant at [PlusAuth Dashboard](https://dashboard.plusauth.com) |
| 65 | +2. Navigate to `Clients` tab and create a client of type `Native Application`. |
| 66 | +3. Go to details page of the client that you've just created and set the following fields as: |
| 67 | + |
| 68 | +- Redirect Uris: `${your-application-id}/callback` |
| 69 | +- Post Logout Redirect Uris: `${your-application-id}/callback` |
| 70 | + |
| 71 | + |
| 72 | +Done! Note your 'Client Id' and 'domain' for library configuration later. |
| 73 | + |
| 74 | +## Configuration |
| 75 | + |
| 76 | +The entry point of the library is the OIDC object. OIDC object can be created by using the OIDCBuilder. |
| 77 | + |
| 78 | +```java |
| 79 | +OIDC oidc = new OIDCBuilder( |
| 80 | + context, |
| 81 | + "your-client-id", |
| 82 | + "your-oidc-domain") |
| 83 | + .build(); |
| 84 | +``` |
| 85 | + |
| 86 | + |
| 87 | +## Login |
| 88 | + |
| 89 | +By default library uses Authorization Code + [PKCE](https://tools.ietf.org/html/rfc7636) flow for maximum security. |
| 90 | +Library wraps all steps of auth flow in to a single call: |
| 91 | + |
| 92 | +```java |
| 93 | +oidc.login(context, new LoginRequest(), new AuthenticationCallback() { |
| 94 | + @Override |
| 95 | + public void onSuccess(Credentials credentials) { |
| 96 | + runOnUiThread(()->{ |
| 97 | + // do ui related work |
| 98 | + }); |
| 99 | + } |
| 100 | + |
| 101 | + @Override |
| 102 | + public void onFailure(AuthenticationException e) { |
| 103 | + Log.e("TAG", "Login failed", e); |
| 104 | + } |
| 105 | + |
| 106 | + }); |
| 107 | +``` |
| 108 | + |
| 109 | +OIDC will open a browser custom tab(if supported) or browser page showing them your OIDC login page and users will be returned back to the app after they complete the login. |
| 110 | + |
| 111 | +After successful response credentials will be locally stored to skip the OIDC flow at next login. |
| 112 | +If there are valid credentials in Storage they will be returned instead, skipping OIDC exchange. |
| 113 | + |
| 114 | + |
| 115 | +This call is asychronous and requires a callback. If you want to do ui related work in callback you must use Activity.runOnUiThread to switch back to main thread since login request is executed in a seperate thread. |
| 116 | + |
| 117 | +## Logout |
| 118 | + |
| 119 | +Logout call is similar to login: |
| 120 | + |
| 121 | +```java |
| 122 | +oidc.logout(this, new LogoutRequest(), new VoidCallback() { |
| 123 | + @Override |
| 124 | + public void onSuccess(Void aVoid) { |
| 125 | + runOnUiThread(()->{ |
| 126 | + // do ui related work |
| 127 | + }); |
| 128 | + } |
| 129 | + |
| 130 | + @Override |
| 131 | + public void onFailure(AuthenticationException e) { |
| 132 | + Log.e("TAG", "Logout failed", e); |
| 133 | +}); |
| 134 | +``` |
| 135 | + |
| 136 | +OIDC will momenrarily open a browser custom tab(if supported) or page of OIDC logout page and users will be immediately returned back to the app. This clears users browser session. |
| 137 | + |
| 138 | +After successful response credentials will be locally removed. This completes the two step logout process. |
| 139 | +If there aren't valid credentials in Storage request will fail. |
| 140 | + |
| 141 | +If you have them, you can revoke your refresh tokens using the [Revoke tokens](#Revoking-a-Token) method from Api. |
| 142 | + |
| 143 | + |
| 144 | +This call is asychronous and requires a callback. If you want to do ui related work in callback you must use Activity.runOnUiThread to switch back to main thread since login request is executed in a seperate thread. |
| 145 | + |
| 146 | +## Api |
| 147 | + |
| 148 | +Api class wraps common OIDC calls and provides easy to use interface. Requests can be called both asynchronously or synchronously. |
| 149 | +No arg method overloads uses provided storage instance |
| 150 | +to get required tokens. |
| 151 | + |
| 152 | +Obtain api instance: |
| 153 | + |
| 154 | +```java |
| 155 | +Api api = oidc.getApi(); |
| 156 | +``` |
| 157 | + |
| 158 | +If you create the api instance manually, make sure to set credentials manager so the no arg overloads can be used, otherwise you will get null pointer errors(also could occur if storage does not have credentials). |
| 159 | + |
| 160 | +### Get User Info |
| 161 | + |
| 162 | +Get User Profile information using [/userinfo](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) endpoint: |
| 163 | + |
| 164 | +```java |
| 165 | +api.userInfo().call(new PACallback<UserProfile, AuthenticationException>() { |
| 166 | + @Override |
| 167 | + public void onSuccess(UserProfile userProfile) { |
| 168 | + runOnUiThread(() -> { |
| 169 | + // do ui related work |
| 170 | + }); |
| 171 | + } |
| 172 | + |
| 173 | + @Override |
| 174 | + public void onFailure(AuthenticationException e) { |
| 175 | + Log.e("TAG", "Could not get profile", e); |
| 176 | + } |
| 177 | +}); |
| 178 | +``` |
| 179 | +### Exchange auth token for credentials |
| 180 | + |
| 181 | +Sends auth token to /token endpoint of the provider in exchange for credentials. |
| 182 | + |
| 183 | +```java |
| 184 | +api.token("your-auth-token").call(new AuthenticationCallback() { |
| 185 | + @Override |
| 186 | + public void onSuccess(Credentials credentials) { |
| 187 | + runOnUiThread(() -> { |
| 188 | + // do ui related work |
| 189 | + }); |
| 190 | + } |
| 191 | + |
| 192 | + @Override |
| 193 | + public void onFailure(AuthenticationException e) { |
| 194 | + Log.e("TAG", "Could exchange auth token", e); |
| 195 | + } |
| 196 | +}); |
| 197 | +``` |
| 198 | + |
| 199 | + |
| 200 | +### Renew Credential with a Refresh Token |
| 201 | + |
| 202 | +Renew your credentials using a refresh token. Note that 'offline_access' scope is required to get a refresh refresh token at login, which is added by default. |
| 203 | + |
| 204 | +```java |
| 205 | + |
| 206 | +api.renewAuth().call(new AuthenticationCallback() { |
| 207 | + @Override |
| 208 | + public void onSuccess(Credentials credentials) { |
| 209 | + runOnUiThread(() -> { |
| 210 | + // do ui related work |
| 211 | + }); |
| 212 | + } |
| 213 | + |
| 214 | + @Override |
| 215 | + public void onFailure(AuthenticationException e) { |
| 216 | + Log.e("TAG", "Could not renew auth", e); |
| 217 | + } |
| 218 | +}); |
| 219 | +``` |
| 220 | + |
| 221 | +### Revoke a Token |
| 222 | + |
| 223 | +Revoke a token using the revokeToken method. No arg overload only revokes the stored refresh token: |
| 224 | + |
| 225 | +```java |
| 226 | +api.revokeToken().call(new VoidCallback() { |
| 227 | + @Override |
| 228 | + public void onSuccess(Void aVoid) { |
| 229 | + runOnUiThread(() -> { |
| 230 | + // do ui related work |
| 231 | + }); |
| 232 | + } |
| 233 | + |
| 234 | + @Override |
| 235 | + public void onFailure(AuthenticationException e) { |
| 236 | + Log.e("TAG", "Could not revoke token", e); |
| 237 | + } |
| 238 | +}); |
| 239 | +``` |
| 240 | + |
| 241 | +## Advance Usage |
| 242 | + |
| 243 | + |
| 244 | +### Storage |
| 245 | + |
| 246 | +By default library uses SharedPreferences in Private mode(can only be accessed by this application) for persistence. You are free to implement the Storage interface with your preferred storage backend. |
| 247 | + |
| 248 | + |
| 249 | +Configure your Storage: |
| 250 | + |
| 251 | +```java |
| 252 | + |
| 253 | +OIDC oidc = new OIDCBuilder( |
| 254 | + context, |
| 255 | + "your-client-id", |
| 256 | + "your-oidc-domain") |
| 257 | + .setStorage(new Storage() { |
| 258 | + @Override |
| 259 | + public void write(@NonNull String s, @Nullable String s1) { |
| 260 | + |
| 261 | + } |
| 262 | + |
| 263 | + @Override |
| 264 | + public void delete(@NonNull String s) { |
| 265 | + |
| 266 | + } |
| 267 | + |
| 268 | + @Override |
| 269 | + public String read(@NonNull String s) { |
| 270 | + return null; |
| 271 | + } |
| 272 | + }) |
| 273 | + .build(); |
| 274 | +``` |
| 275 | + |
| 276 | +### Encryption |
| 277 | + |
| 278 | +By default library does not use any encryption when storing credentials. You are free to implement the Encryptor interface with your preferred encryption method. We provide AESEncryptor class for 256 bit AES GCM implementation which can be used from Android Api 23 and up. |
| 279 | + |
| 280 | +Configure your Encryptor: |
| 281 | + |
| 282 | +```java |
| 283 | + |
| 284 | +OIDC oidc = new OIDCBuilder( |
| 285 | + context, |
| 286 | + "your-client-id", |
| 287 | + "your-oidc-domain") |
| 288 | + .setEncryptor(new Encryptor() { |
| 289 | + @Override |
| 290 | + public String encrypt(String s) { |
| 291 | + return null; |
| 292 | + } |
| 293 | + |
| 294 | + @Override |
| 295 | + public String decrypt(String s) { |
| 296 | + return null; |
| 297 | + } |
| 298 | + }) |
| 299 | + .build(); |
| 300 | +``` |
| 301 | + |
| 302 | +## Example App |
| 303 | + |
| 304 | +We built a very simple app demonstrating login/logout and fetching user info. Check it out [here](https://github.com/PlusAuth/plusauth-oidc-android-example). |
| 305 | + |
| 306 | +## Acknowledgements |
| 307 | + |
| 308 | +Design of this library was inspired by awesome OSS libraries such as [AppAuth](https://github.com/openid/AppAuth-Android). |
| 309 | + |
| 310 | +If you have used OIDC with any Android library, you might have felt overwhelmed. We did too. That is why we built PlusAuth OIDC Library For Android. We hope to lower the entry barrier for OIDC complexity so that all developers could enjoy benefits that it brings. |
| 311 | + |
| 312 | +# License |
| 313 | + |
| 314 | +This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. |
| 315 | + |
0 commit comments