Skip to content

Commit df56616

Browse files
authored
Fix client authentication header on Android (#64)
* Include client auth when clientSecret is included in request * Add the correct authentication header for refresh requests * Refactor * Add instructions in the readme for authenticating with Fitbit * Fix typo
1 parent 5cace9d commit df56616

File tree

2 files changed

+85
-22
lines changed

2 files changed

+85
-22
lines changed

README.md

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,21 @@ React Native bridge for [AppAuth-iOS](https://github.com/openid/AppAuth-iOS) and
1515
[OpenID Connect](http://openid.net/specs/openid-connect-core-1_0.html) providers.
1616

1717
This library _should_ support any OAuth provider that implements the
18-
[OAuth2 spec](https://tools.ietf.org/html/rfc6749#section-2.2) and it has been tested with:
18+
[OAuth2 spec](https://tools.ietf.org/html/rfc6749#section-2.2).
1919

20+
### Tested OpenID providers:
21+
These providers are OpenID compliant, which means you can use [autodiscovery](https://openid.net/specs/openid-connect-discovery-1_0.html).
2022
* [Identity Server4](https://demo.identityserver.io/) ([Example configuration](#identity-server-4))
2123
* [Identity Server3](https://github.com/IdentityServer/IdentityServer3) ([Example configuration](#identity-server-3))
2224
* [Google](https://developers.google.com/identity/protocols/OAuth2)
2325
([Example configuration](#google))
2426
* [Okta](https://developer.okta.com) ([Example configuration](#okta))
2527
* [Keycloak](http://www.keycloak.org/) ([Example configuration](#keycloak))
2628

27-
The library uses auto-discovery which means it relies on the the
28-
[.well-known/openid-configuration](https://openid.net/specs/openid-connect-discovery-1_0.html)
29-
endpoint to discover all auth endpoints automatically. It will be possible to extend the library
30-
later to add custom configuration.
29+
### Tested OAuth2 providers:
30+
These providers implement the OAuth2 spec, but are not OpenID providers, which means you must configure the authorization and token endpoints yourself.
31+
* [Uber](https://developer.uber.com/docs/deliveries/guides/three-legged-oauth) ([Example configuration](#uber))
32+
* [Fitbit](https://dev.fitbit.com/build/reference/web-api/oauth2/) ([Example configuration](#fitbit))
3133

3234
## Why you may want to use this library
3335

@@ -585,6 +587,43 @@ await revoke(config, {
585587
});
586588
```
587589

590+
### Fitbit
591+
592+
Fitbit provides an OAuth 2.0 endpoint for logging in with a Fitbit user's credentials. You'll need to first [register your Fitbit application here](https://dev.fitbit.com/apps/new).
593+
594+
Please note:
595+
596+
* Fitbit does not provide a OIDC discovery endpoint, so `serviceConfiguration` is used instead.
597+
* Fitbit OAuth requires a [client secret](#note-about-client-secrets).
598+
599+
```js
600+
const config = {
601+
clientId: 'your-client-id-generated-by-uber',
602+
clientSecret: 'your-client-secret-generated-by-fitbit',
603+
redirectUrl: 'com.whatever.url.you.configured.in.uber.oauth://redirect', //note: path is required
604+
scopes: ['activity', 'sleep'],
605+
serviceConfiguration: {
606+
authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize',
607+
tokenEndpoint: 'https://api.fitbit.com/oauth2/token',
608+
revocationEndpoint: 'https://api.fitbit.com/oauth2/revoke'
609+
}
610+
};
611+
612+
// Log in to get an authentication token
613+
const authState = await authorize(config);
614+
615+
// Refresh token
616+
const refreshedState = await refresh(config, {
617+
refreshToken: authState.refreshToken,
618+
});
619+
620+
// Revoke token
621+
await revoke(config, {
622+
tokenToRevoke: refreshedState.refreshToken
623+
});
624+
```
625+
626+
588627
## Contributors
589628

590629
Thanks goes to these wonderful people

android/src/main/java/com/reactlibrary/RNAppAuthModule.java

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
import net.openid.appauth.AuthorizationResponse;
2525
import net.openid.appauth.AuthorizationService;
2626
import net.openid.appauth.AuthorizationServiceConfiguration;
27+
import net.openid.appauth.ClientAuthentication;
28+
import net.openid.appauth.ClientSecretBasic;
2729
import net.openid.appauth.ResponseTypeValues;
2830
import net.openid.appauth.TokenResponse;
2931
import net.openid.appauth.TokenRequest;
@@ -42,6 +44,7 @@ public class RNAppAuthModule extends ReactContextBaseJavaModule implements Activ
4244
private Promise promise;
4345
private Boolean dangerouslyAllowInsecureHttpRequests;
4446
private Map<String, String> additionalParametersMap;
47+
private String clientSecret;
4548

4649
public RNAppAuthModule(ReactApplicationContext reactContext) {
4750
super(reactContext);
@@ -75,6 +78,7 @@ public void authorize(
7578
this.promise = promise;
7679
this.dangerouslyAllowInsecureHttpRequests = dangerouslyAllowInsecureHttpRequests;
7780
this.additionalParametersMap = additionalParametersMap;
81+
this.clientSecret = clientSecret;
7882

7983
// when serviceConfiguration is provided, we don't need to hit up the OpenID well-known id endpoint
8084
if (serviceConfiguration != null) {
@@ -159,6 +163,7 @@ public void refresh(
159163
scopesString,
160164
redirectUrl,
161165
additionalParametersMap,
166+
clientSecret,
162167
promise
163168
);
164169
} catch (Exception e) {
@@ -186,6 +191,7 @@ public void onFetchConfigurationCompleted(
186191
scopesString,
187192
redirectUrl,
188193
additionalParametersMap,
194+
clientSecret,
189195
promise
190196
);
191197
}
@@ -216,21 +222,28 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode,
216222
AuthorizationService authService = new AuthorizationService(this.reactContext, configuration);
217223

218224
TokenRequest tokenRequest = response.createTokenExchangeRequest(this.additionalParametersMap);
219-
authService.performTokenRequest(
220-
tokenRequest,
221-
new AuthorizationService.TokenResponseCallback() {
222-
223-
@Override
224-
public void onTokenRequestCompleted(
225-
TokenResponse resp, AuthorizationException ex) {
226-
if (resp != null) {
227-
WritableMap map = tokenResponseToMap(resp);
228-
authorizePromise.resolve(map);
229-
} else {
230-
promise.reject("RNAppAuth Error", "Failed exchange token", ex);
231-
}
232-
}
233-
});
225+
226+
AuthorizationService.TokenResponseCallback tokenResponseCallback = new AuthorizationService.TokenResponseCallback() {
227+
228+
@Override
229+
public void onTokenRequestCompleted(
230+
TokenResponse resp, AuthorizationException ex) {
231+
if (resp != null) {
232+
WritableMap map = tokenResponseToMap(resp);
233+
authorizePromise.resolve(map);
234+
} else {
235+
promise.reject("RNAppAuth Error", "Failed exchange token", ex);
236+
}
237+
}
238+
};
239+
240+
if (this.clientSecret != null) {
241+
ClientAuthentication clientAuth = new ClientSecretBasic(this.clientSecret);
242+
authService.performTokenRequest(tokenRequest, clientAuth, tokenResponseCallback);
243+
244+
} else {
245+
authService.performTokenRequest(tokenRequest, tokenResponseCallback);
246+
}
234247

235248
}
236249
}
@@ -280,6 +293,7 @@ private void refreshWithConfiguration(
280293
final String scopesString,
281294
final String redirectUrl,
282295
final Map<String, String> additionalParametersMap,
296+
final String clientSecret,
283297
final Promise promise
284298
) {
285299

@@ -301,7 +315,8 @@ private void refreshWithConfiguration(
301315
TokenRequest tokenRequest = tokenRequestBuilder.build();
302316

303317
AuthorizationService authService = new AuthorizationService(context, appAuthConfiguration);
304-
authService.performTokenRequest(tokenRequest, new AuthorizationService.TokenResponseCallback() {
318+
319+
AuthorizationService.TokenResponseCallback tokenResponseCallback = new AuthorizationService.TokenResponseCallback() {
305320
@Override
306321
public void onTokenRequestCompleted(@Nullable TokenResponse response, @Nullable AuthorizationException ex) {
307322
if (response != null) {
@@ -311,7 +326,16 @@ public void onTokenRequestCompleted(@Nullable TokenResponse response, @Nullable
311326
promise.reject("RNAppAuth Error", "Failed refresh token");
312327
}
313328
}
314-
});
329+
};
330+
331+
332+
if (clientSecret != null) {
333+
ClientAuthentication clientAuth = new ClientSecretBasic(this.clientSecret);
334+
authService.performTokenRequest(tokenRequest, clientAuth, tokenResponseCallback);
335+
336+
} else {
337+
authService.performTokenRequest(tokenRequest, tokenResponseCallback);
338+
}
315339
}
316340

317341
/*

0 commit comments

Comments
 (0)