Git Product home page Git Product logo

react-native-app-auth's Introduction

React Native App Auth

React native bridge for AppAuth - an SDK for communicating with OAuth2 providers

npm package version Maintenance Status Workflow Status

This versions supports [email protected]+. The last pre-0.63 compatible version is v5.1.3.

React Native bridge for AppAuth-iOS and AppAuth-Android SDKS for communicating with OAuth 2.0 and OpenID Connect providers.

This library should support any OAuth provider that implements the OAuth2 spec.

We only support the Authorization Code Flow.

Check out the full documentation here!

Tested OpenID providers

These providers are OpenID compliant, which means you can use autodiscovery.

Tested OAuth2 providers

These providers implement the OAuth2 spec, but are not OpenID providers, which means you must configure the authorization and token endpoints yourself.

Why you may want to use this library

AppAuth is a mature OAuth client implementation that follows the best practices set out in RFC 8252 - OAuth 2.0 for Native Apps including using ASWebAuthenticationSession and SFSafariViewController on iOS, and Custom Tabs on Android. WebViews are explicitly not supported due to the security and usability reasons explained in Section 8.12 of RFC 8252.

AppAuth also supports the PKCE ("Pixy") extension to OAuth which was created to secure authorization codes in public clients when custom URI scheme redirects are used.

To learn more, read this short introduction to OAuth and PKCE on the Formidable blog.

Contributing

Please see our contributing guide.

Running the iOS app

After cloning the repository, run the following:

cd react-native-app-auth/Example
yarn
(cd ios && pod install)
npx react-native run-ios

Running the Android app

After cloning the repository, run the following:

cd react-native-app-auth/Example
yarn
npx react-native run-android

Notes

  • You have to have the emulator open before running the last command. If you have difficulty getting the emulator to connect, open the project from Android Studio and run it through there.
  • ANDROID: When integrating with a project that utilizes deep linking (e.g. React Navigation deep linking), update the redirectUrl in your config and the appAuthRedirectScheme value in build.gradle to use a custom scheme so that it differs from the scheme used in your deep linking intent-filter as seen here.

Example:

// build.gradle
android {
  defaultConfig {
    manifestPlaceholders = [
      appAuthRedirectScheme: 'io.identityserver.demo.auth'
    ]
  }
}

Maintenance Status

Active: Nearform is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.

react-native-app-auth's People

Contributors

badsyntax avatar carbonrobot avatar carrybit avatar cpresler avatar dependabot[bot] avatar eliaslecomte avatar faizplus avatar friederbluemle avatar github-actions[bot] avatar hadnazzar avatar hakonk avatar imranolas avatar iyaseen avatar janpieterz avatar jasonpaulos avatar jay-a-mcbee avatar jevakallio avatar jovidecroock avatar jpdriver avatar jspizziri avatar kadikraman avatar kitten avatar kpelelis avatar masiddee avatar mooreds avatar robwalkerco avatar sheepsteak avatar shervanator avatar siddsarkar avatar williamtorres1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-native-app-auth's Issues

Allow discovery to be executed explicitly and keep configuration around

It'd be great if the discovery could be skipped by replacing its data with a custom input (JS) or by calling a method at a custom point in time (ahead of time).

The latter can easily be achieved by caching the result of the service discovery on the native side.

The former would require us to create a serialiser (input & output) for the service configuration.

Feature request: Automatic renew

Hi, I've got another idea working with the lib the other day.

What do you think about adding ability to do automatic renew?
The way I see it working would be a method called something like automaticRefresh that would accept config (like normal refresh) plus existing token response (to know expiration) plus a callback that fires on refresh.

I think it's pretty straight forward, let me know what you think ;)

installation problem

After I run it after installation. I got the following error

Task :app:compileDebugJavaWithJavac
C:\Kaizen.App\android\app\src\main\java\com\kaizenapp\MainApplication.java:6: error: package com.reactlibrary does not exist
import com.reactlibrary.RNAppAuthPackage;
^
C:\Kaizen.App\android\app\src\main\java\com\kaizenapp\MainApplication.java:30: error: cannot find symbol
new RNAppAuthPackage(),
^
symbol: class RNAppAuthPackage

I also upgrade the build tool and gradle version but still not working. any suggestion?

Network Profiling

I just tried the "Network Profiler" built into Android Studio, but it didn't detect any traffic. Possible reason was:

Currently, the Network Profiler supports only the HttpURLConnection and OkHttp libraries for network connections.

How do others detect/monitor the outgoing network traffic?

Failed exchange token - Fitbit

I changed the Example app with Fitbit config. I can get the authentication page of Fitbit after press the "Authorize" button. After login and allowed, it can redirect to the app. Here I get the error: Failed to log in, Failed to exchange token.

I think this error happened during token exchange. But I'm sure the clientSecret and tokenEndpoint are correct(based on Access Token Request).
Any suggestion will be helpful.

image

App.js

import React, { Component } from 'react';
import { UIManager, LayoutAnimation, Alert } from 'react-native';
import { authorize, refresh, revoke } from 'react-native-app-auth';
import { Page, Button, ButtonContainer, Form, Heading } from './components';

UIManager.setLayoutAnimationEnabledExperimental &&
  UIManager.setLayoutAnimationEnabledExperimental(true);

type State = {
  hasLoggedInOnce: boolean,
  accessToken: ?string,
  accessTokenExpirationDate: ?string,
  refreshToken: ?string
};



const config = {
  clientId: 'XXXXXX',
  clientSecret: 'XXXXXX',
  redirectUrl: 'com.XXXXXX://fit',
  additionalParameters: {},
  scopes: ['activity', 'sleep'],
  serviceConfiguration: {
    authorizationEndpoint: 'https://www.fitbit.com/oauth2/authorize',
    tokenEndpoint: 'https://api.fitbit.com/oauth2/token'
  }
};

export default class App extends Component<{}, State> {
  state = {
    hasLoggedInOnce: false,
    accessToken: '',
    accessTokenExpirationDate: '',
    refreshToken: ''
  };

  animateState(nextState: $Shape<State>, delay: number = 0) {
    setTimeout(() => {
      this.setState(() => {
        LayoutAnimation.easeInEaseOut();
        return nextState;
      });
    }, delay);
  }

  authorize = async () => {
    try {
      console.log('authorize-log');
      const authState = await authorize(config);
      console.log('authorize-end');
      this.animateState(
        {
          hasLoggedInOnce: true,
          accessToken: authState.accessToken,
          accessTokenExpirationDate: authState.accessTokenExpirationDate,
          refreshToken: authState.refreshToken
        },
        500
      );
    } catch (error) {
      Alert.alert('Failed to log in', error.message);
      console.log(error);
    }
  };

  refresh = async () => {
    try {
      const authState = await refresh(config, {
        refreshToken: this.state.refreshToken
      });

      this.animateState({
        accessToken: authState.accessToken || this.state.accessToken,
        accessTokenExpirationDate:
          authState.accessTokenExpirationDate || this.state.accessTokenExpirationDate,
        refreshToken: authState.refreshToken || this.state.refreshToken
      });
    } catch (error) {
      Alert.alert('Failed to refresh token', error.message);
    }
  };

  revoke = async () => {
    try {
      await revoke(config, {
        tokenToRevoke: this.state.accessToken,
        sendClientId: true
      });
      this.animateState({
        accessToken: '',
        accessTokenExpirationDate: '',
        refreshToken: ''
      });
    } catch (error) {
      Alert.alert('Failed to revoke token', error.message);
    }
  };

  render() {
    const { state } = this;
    return (
      <Page>
        {!!state.accessToken ? (
          <Form>
            <Form.Label>accessToken</Form.Label>
            <Form.Value>{state.accessToken}</Form.Value>
            <Form.Label>accessTokenExpirationDate</Form.Label>
            <Form.Value>{state.accessTokenExpirationDate}</Form.Value>
            <Form.Label>refreshToken</Form.Label>
            <Form.Value>{state.refreshToken}</Form.Value>
          </Form>
        ) : (
          <Heading>{state.hasLoggedInOnce ? 'Goodbye.' : 'Hello, stranger.'}</Heading>
        )}

        <ButtonContainer>
          {!state.accessToken && (
            <Button onPress={this.authorize} text="Authorize" color="#DA2536" />
          )}
          {!!state.refreshToken && <Button onPress={this.refresh} text="Refresh" color="#24C2CB" />}
          {!!state.accessToken && <Button onPress={this.revoke} text="Revoke" color="#EF525B" />}
        </ButtonContainer>
      </Page>
    );
  }
}

Error: Failed exchange token, authenticating for access to google drive

I changed the included example app to authenticate with google, also enabled google drive API and other google console steps.

On clicking on the Authorize button, I get the google signin window and after successful login I get the grant permission to google drive, but as soon as I click "allow", the webpage closes back to the app and I get the red screen with the error.

I get the below error:

image

App.js


// @flow

import React, { Component } from 'react';
import { UIManager, LayoutAnimation } from 'react-native';
import AppAuth from 'react-native-app-auth';
import { Page, Button, ButtonContainer, Form, Heading } from './components';

UIManager.setLayoutAnimationEnabledExperimental &&
  UIManager.setLayoutAnimationEnabledExperimental(true);

const scopes = ['https://www.googleapis.com/auth/drive.appfolder'];

type State = {
  hasLoggedInOnce: boolean,
  accessToken: ?string,
  accessTokenExpirationDate: ?string,
  refreshToken: ?string
};

export default class App extends Component<{}, State> {
  auth = new AppAuth({
    issuer: 'https://accounts.google.com',
    clientId: 'YYYYYYYYYYYYY-XXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com',
    redirectUrl: 'com.googleusercontent.apps.YYYYYYYYYYYYY-XXXXXXXXXXXXXXXXXXXXXXXXXX:/oauth2redirect/google'
  });

  state = {
    hasLoggedInOnce: false,
    accessToken: '',
    accessTokenExpirationDate: '',
    refreshToken: ''
  };

  animateState(nextState: $Shape<State>, delay: number = 0) {
    setTimeout(() => {
      this.setState(() => {
        LayoutAnimation.easeInEaseOut();
        return nextState;
      });
    }, delay);
  }

  authorize = async () => {
    try {
      const authState = await this.auth.authorize(scopes);
      // this.animateState(
      //   {
      //     hasLoggedInOnce: true,
      //     accessToken: authState.accessToken,
      //     accessTokenExpirationDate: authState.accessTokenExpirationDate,
      //     refreshToken: authState.refreshToken
      //   },
      //   500
      // );
    } catch (error) {
      console.error(error);
    }
  };

  refresh = async () => {
    try {
      const authState = await this.auth.refresh(this.state.refreshToken, scopes);
      this.animateState({
        accessToken: authState.accessToken || this.state.accessToken,
        accessTokenExpirationDate:
          authState.accessTokenExpirationDate || this.state.accessTokenExpirationDate,
        refreshToken: authState.refreshToken || this.state.refreshToken
      });
    } catch (error) {
      console.error(error);
    }
  };

  revoke = async () => {
    try {
      await this.auth.revokeToken(this.state.accessToken, true);
      this.animateState({
        accessToken: '',
        accessTokenExpirationDate: '',
        refreshToken: ''
      });
    } catch (error) {
      console.error(error);
    }
  };

  render() {
    const { state } = this;
    return (
      <Page>
        {!!state.accessToken ? (
          <Form>
            <Form.Label>accessToken</Form.Label>
            <Form.Value>{state.accessToken}</Form.Value>
            <Form.Label>accessTokenExpirationDate</Form.Label>
            <Form.Value>{state.accessTokenExpirationDate}</Form.Value>
            <Form.Label>refreshToken</Form.Label>
            <Form.Value>{state.refreshToken}</Form.Value>
          </Form>
        ) : (
          <Heading>{state.hasLoggedInOnce ? 'Goodbye.' : 'Hello, stranger.'}</Heading>
        )}

        <ButtonContainer>
          {!state.accessToken && (
            <Button onPress={this.authorize} text="Authorize" color="#DA2536" />
          )}
          {!!state.refreshToken && <Button onPress={this.refresh} text="Refresh" color="#24C2CB" />}
          {!!state.accessToken && <Button onPress={this.revoke} text="Revoke" color="#EF525B" />}
        </ButtonContainer>
      </Page>
    );
  }
}

android/app/build.gradle

Relevant changes:


 manifestPlaceholders = [
                appAuthRedirectScheme: "com.googleusercontent.apps.YYYYYYYYYYYYY-XXXXXXXXXXXXXXXXXXXXXXXXXX"
        ]  

Failed to fetch configuration

Hi!
I'm trying to make uber oauth2 work but I keep getting this error (currently testing on android).

Error: Failed to fetch configuration
    at createErrorFromErrorData (NativeModules.js:123)
    at NativeModules.js:80
    at MessageQueue.__invokeCallback (MessageQueue.js:346)
    at MessageQueue.js:132
    at MessageQueue.__guard (MessageQueue.js:262)
    at MessageQueue.invokeCallbackAndReturnFlushedQueue (MessageQueue.js:131)
    at debuggerWorker.js:72

From what I've seen in issues, this is a problem with 'issuer' variable. Seems like I can't understand what string I should use there.

Here is my code

    const scopes = ["request"];
    const appAuth = new AppAuth({
      issuer: "https://login.uber.com/oauth/v2/authorize",
      clientId: settings.UberClientId,
      redirectUrl: "myApp:/oauth"
    })

    appAuth.authorize(scopes).then((response)=>{
      console.log('response=>', response)
    }).catch((reject)=>{
      console.log('reject=>', reject)
    });

Here is the screenshot of uber endpoints
image

Logo

Every open source library needs a logo. Well, that's not true. But it would be cool to have one anyway.

Multiple auth requests causes 'Response state param did not match request state'

On my login page, I have a link to a register page. On this page, I have links back to the login page.

This is done by linking back into the app where we call authorize again in order to navigate the webview back to the login page.

This then authenticates but an exception is thrown from onActivityResult

   /*
     * Called when the OAuth browser activity completes
     */
    @Override
    public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
        if (requestCode == 0) {
            AuthorizationResponse response = AuthorizationResponse.fromIntent(data);
            AuthorizationException exception = AuthorizationException.fromIntent(data);
            if (exception != null) {
                promise.reject("RNAppAuth Error", "Failed to authenticate", exception); <-- REJECTION HERE
                return;
            }

Response state param did not match request state

Multiple apps with same appAuthRedirectScheme

Hello again :) This is more of a question than an issue. I would use stack overflow but I think it's still small enough that you won't throw rocks at me :D Title of the questions sums it up perfectly I guess.
I have a test scenario where I have 2 apps with the same appAuthRedirectScheme. I clicked (once upon a time) to remember my choice of opening given appAuthRedirectScheme using app 1.

Therefore now if I use app2, it goes to redirect, I do auth flow, then it comes back but to app1.

Obviously this would be huge security risk, yet I believe it's just lack of my knowledge about this lib or maybe android in general.

What would be the "right" way to handle this kind of situation?

await authorize() call never returns on Android after I finalize or cancel the login

I am developing a Create React Native App/Expo app (versions: RN 0.52.0, Expo 25) and I have seemingly correctly set up my AndroidManifest.xml with the correct net.openid.appauth activity; I think I know this because after I login inside the authorization webview, I'm redirected, the Android app starts back up, and I can see the redirect code/url in my logcat.

However, nothing happens after that - the promise just never returns and the token isn't requested. The promise from RNAppAuth doesn't return even if I cancel the authorization request, by the way. This isn't isolated to my custom identity provider - it's the same thing for Google, too. I believe I'm using the latest version of the Android-AppAuth and react-native-app-auth libraries, but it's very hard to determine precisely which version of Android-AppAuth I'm using since it's an existing ExpoKit dependency as well.

Difference AppDelegate.m

Tbh I am very new to react native and thus I get insecure very quickly if any lines in a readme are not the way I expect them to be. In this case, you have these lines:

- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<NSString *, id> *)options {

But the default from RN looks like this:

- (BOOL)application:(UIApplication *)application 
            didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

Since I have only a very limited idea, what any of this does, I don't know, if you expect me to replace the default line with yours or only parts of it, or if yours is just older.

Thanks in advance!

Small error in installation guide for Android

I believe there is a small error in the (manual) installation guide. Under Getting Started -> Manual Installation -> Android it says to add an import and an RNAppAuthPackage() to MainActivity.java.

However, I believe that should be MainApplication.java. The example project has added those lines in the MainApplication.java and the getPackages() method is also only available in the MainApplication.java and not in the MainActivity.java.

‘AppAuth.h’ file not found

It seems that the import statement needs to specify the framework name.

It should be <AppAuth/AppAuth.h> instead of ”AppAuth.h"

Better error handling for required SSL

Report type:
BUG

Summary:
SSL appears to be enforced by AppAuth-Android library therefore you cannot use http for your idp. Makes sense but remember this limitation exists for localhost as well which is quite a letdown for dev and testing.

Expected result:
If you try to connect to NON-SSL idp, a meaningful error message should be presented to the developer.

Actual result:
App crashes with "Unfortunately, XXX has stopped. OK"

New problem after #63

The fix in #63 (v2.3.0) works well on fitbit. But after I switched back to Jawbone API, it failed again with "failed exchange token". I tested Jawbone API on (v2.2.1), it worked well. I think the fix in #63 generates some new issues.

idToken is missing on Android

idToken is missing in TokenResponse handling of authorize() on Android (the same mapping is used for authorize() and refresh())

WritableMap map = Arguments.createMap();
map.putString("accessToken", response.accessToken);
map.putString("accessTokenExpirationDate", expirationDateString);
map.putString("refreshToken", response.refreshToken);

IOS uses different mapping for authorize() ...

NSDictionary *authStateDict = @{
@"accessToken": authState.lastTokenResponse.accessToken,
@"tokenType": authState.lastTokenResponse.tokenType,
@"accessTokenExpirationDate": exporationDateString,
@"idToken": authState.lastTokenResponse.idToken,
@"refreshToken": authState.lastTokenResponse.refreshToken ? authState.lastTokenResponse.refreshToken : @"",
@"additionalParameters": authState.lastTokenResponse.additionalParameters,

and refresh() ...

resolve(@{
@"accessToken": response.accessToken ? response.accessToken : @"",
@"refreshToken": response.refreshToken ? response.refreshToken : @"",
@"accessTokenExpirationDate": exporationDateString,
});

Proposal: v2.0.0

Get rid of the AppAuth class and make the api function-based

Currently API:

import AppAuth from 'react-native-app-auth';

const refreshTokenConfig = {
  issuer: <ISSUER>,
  redirectUrl: <REDIRECT_URL>,
  clientId: <CLIENT_ID>,
  additionalParameters: <ADDITIONAL_PARAMETERS>
};
const appAuth = new AppAuth(config);

// authorize
const scopes = [SCOPES];
const result = await appAuth.authorize(scopes);

// refresh
const scopes = [SCOPES];
const result = await appAuth.refresh(scopes);

// revoke
const tokenToRevoke = <TOKEN>;
const sendClientId = BOOLEAN;
const result = await appAuth.revokeToken(tokenToRevoke, sendClientId = false);

Proposed API:

import { authorize, refresh, revokeToken } from 'react-native-app-auth';

// authorize
const authorizeConfig = {
  issuer: <ISSUER>,
  redirectUrl: <REDIRECT_URL>,
  clientId: <CLIENT_ID>,
  scopes: [SCOPES],
  additionalParameters: <ADDITIONAL_PARAMETERS>
};
const result = await authorize(authorizeConfig);

// refresh
const refreshTokenConfig = {
  issuer: <ISSUER>,
  redirectUrl: <REDIRECT_URL>,
  clientId: <CLIENT_ID>,
  refreshToken: <TOKEN>,
  scopes: [SCOPES],
  additionalParameters: <ADDITIONAL_PARAMETERS>
};
const result = await refresh(refreshTokenConfig);

// revoke
const revokeTokenConfig = {
  issuer: <ISSUER>,
  clientId: <CLIENT_ID>,
  tokenToRevoke: <TOKEN>,
  sendClientId: BOOLEAN
};
const result = await revokeToken(revokeTokenConfig);

Pros

  • API is easier to grasp, as it gets rid of the extra step of creating a new instance of AppAuth
  • Easier to extend: the config is just an object

Cons

  • There is more repetition
  • Easier to lose track of shared config
  • Config validation has to be done in every exposed function rather than once when instantiating

Feature request: proper support for built-in parameters

I want to use prompt parameter (RFC) to change the auth flow.

I tried using it like this:

const config: {
    issuer: ISSUER_URL,
    clientId: CLIENT_ID,
    redirectUrl: "com.example://postlogin",
    scopes: ["openid", "profile", "email", "offline_access", "phone"],
    additionalParameters: { prompt: "login" }, // trying to set the prompt
},

This works as expected in iOS, however in Android it throws:

net.openid.appauth.Preconditions.checkArgument
Preconditions.java - line 132

java.lang.IllegalArgumentException: Parameter prompt is directly supported via the authorization request builder, use the builder method instead

1 net.openid.appauth.Preconditions.checkArgument Preconditions.java:132
2 net.openid.appauth.AdditionalParamsProcessor.checkAdditionalParams AdditionalParamsProcessor.java:62
3 net.openid.appauth.AuthorizationRequest$Builder.setAdditionalParameters AuthorizationRequest.java:854
4 com.reactlibrary.RNAppAuthModule.authorizeWithConfiguration RNAppAuthModule.java:276
5 com.reactlibrary.RNAppAuthModule.access$000 RNAppAuthModule.java:41
6 com.reactlibrary.RNAppAuthModule$1.onFetchConfigurationCompleted RNAppAuthModule.java:110
7 net.openid.appauth.AuthorizationServiceConfiguration$ConfigurationRetrievalAsyncTask.onPostExecute AuthorizationServiceConfiguration.java:364
8 net.openid.appauth.AuthorizationServiceConfiguration$ConfigurationRetrievalAsyncTask.onPostExecute AuthorizationServiceConfiguration.java:305
9 android.os.AsyncTask.finish AsyncTask.java:667

The reason for that is that the AppAuth-Android supports setting the prompt mode (among others) using a helper and checks that the additionalData don't contain values that should be set using the helper.

I propose introducing config options for the supported parameters.

Better error handling for canceled auth flow

Report type:
BUG

Steps to reproduce:
Start current example app.
Click authorize.
Not abandon authorization (click back)
Observe the red screen of death.

Expected result:
Exception should be thrown, catched and handled via user code. The code in example app indeed is surrounded with try catch block but it appears it's no use for this scenario.

Actual result:
App shows red screen of death
canceledauthflow

Failed refresh token

Hi all

I am quite new to RN as well as app auth but I have previously integrated my Xamarin app with IdentityServer4 using IdentityModel.OidcClient so I have some understanding of the flow.

Please see my following code, the issue I am having is I can not use my saved refresh token to retrieve access token using this.auth.refresh, it always fails unless if I run this.auth.authorize first.

To summarize:
no refresh token => authorize => store refresh token => auth.refresh to retrieve access token => api access = works
retrieve refresh token => auth.refresh to retrieve access token = Failed refresh token

What have I done wrong here?

componentDidMount() {
this.refresh().then(res => {
LayoutAnimation.easeInEaseOut();
this.setState({
isLoading: false,
devices: res || []
});
});
}
async refresh() {
let refreshToken = await AsyncStorage.getItem('refreshToken');
if (refreshToken === null) {
let authState = await this.authorize();
await AsyncStorage.setItem('refreshToken', authState.refreshToken);
refreshToken = authState.refreshToken;
}
try {
const refreshedState = await this.auth.refresh(refreshToken, scopes);
let response = await fetch(baseUrl + '/api/client', {
method: 'get',
headers: {
'Accept': 'application/json',
'Authorization': 'Bearer '+ refreshedState.accessToken
}
});
return await response.json();
} catch (error) {
await AsyncStorage.removeItem('refreshToken');
return await this.refresh();
}
}

async authorize() {
try {
return await this.auth.authorize(scopes);
}
catch(error) {
Alert.alert(
'An error has occured', 'Please try again.'
)
return await this.authorize();
}
}

Identity Server 3 compatibility test

Hi All,

Thanks for guiding me in my previous post, I am able to configure it successfully and run this package on React Native. Here is the new issue I am facing

After I click the sign in button, the identity server login page pop up in webview, but it shows ""The client application is not known or is not authorized" so I copy to url and paste into the browser to see if all the query parameter are correct. Then I found that the "response type" is "code", once I switch to "token code", it works. But how can I do it in this module. I try to go through the document here but I could not find any way to change the response type. Any suggestion?

Thank you in advance.

Localhost identity server 4 setup - possible bug

Hello all. First I'd like to thank you for putting together this lib. It seems to be fairly fresh (91 stars at the time of writing) but it really feels like it will pick up. So far it's by far the best way to authenticate in react-native I found.

It's quite difficult for me to identify with 100% accuracy if this is a bug so I'll try to explain what I am doing.
I started off the example (copied over the app code) which works fine. I wanted to swap the demo server with my own running on my machine. So I swapped the issuer for my http://localhost:53721 and opened the reverse proxy via adb reverse tcp:53721 tcp:53721 so that I can access it on my emulator (API 23 Android 6.0 (Google APIs)). I left the redirectUrl what it was before (redirectUrl: 'io.identityserver.demo:/oauthredirect') partially cause I am not exactly sure what to put there in case of redirect.

What happens when I run the app and click Authorize, it stops for a second and then "Unfortunately, XXX has stopped. OK" Alert message.

While I understand that I might need to fix the redirectUrl both in app and in build.gradle, it still shouldn't fail this critically IMHO. Especially when it's surrounded with try catch block.

Am I doing something wrong or is this a bug? Any input much appreciated.

Idea: Provide functionality (or instructions) to safely store authentication tokens

Currently the focus of this library is to complete the OAuth token exchange. When it comes to securely storing the received tokens, we leave the user to their own devices (pun intended).

This causes an unfortunate accidental security hazard where a user of this library may inadvertently store their refresh tokens in AsyncStorage etc.

I think we should either

  1. Provide instructions on how to store the tokens securely using iOS Keychain / as encrypted values in Android SharedPreferences, or
  2. Implement secure storage functionality into the library itself

native.code client definition for Identityserver4

Hello,
can you provide the native.code client definition for Identityserver4 ?
I'm trying to connect the example react native client in example but i can't succeed to connect the example app project to my server.

my client config :

{
"id" : ObjectId("5a325724a23d27e2a40e3229"),
"Enabled" : true,
"ClientId" : "native.code",
"ProtocolType" : "oidc",
"ClientSecrets" : [],
"RequireClientSecret" : false,
"ClientName" : "Native Client (Code with PKCE)",
"ClientUri" : null,
"LogoUri" : null,
"RequireConsent" : true,
"AllowRememberConsent" : true,
"AllowedGrantTypes" : [
"password"
],
"RequirePkce" : true,
"AllowPlainTextPkce" : false,
"AllowAccessTokensViaBrowser" : false,
"RedirectUris" : [
"io.identityserver.demo",
],
"PostLogoutRedirectUris" : [
"https://notused"
],
"FrontChannelLogoutUri" : null,
"FrontChannelLogoutSessionRequired" : true,
"BackChannelLogoutUri" : null,
"BackChannelLogoutSessionRequired" : true,
"AllowOfflineAccess" : true,
"AllowedScopes" : [
"openid",
"profile",
"email",
"api",
"offline_access"
],
"AlwaysIncludeUserClaimsInIdToken" : false,
"IdentityTokenLifetime" : 300,
"AccessTokenLifetime" : 3600,
"AuthorizationCodeLifetime" : 300,
"AbsoluteRefreshTokenLifetime" : 2592000,
"SlidingRefreshTokenLifetime" : 1296000,
"ConsentLifetime" : null,
"RefreshTokenUsage" : 1,
"UpdateAccessTokenClaimsOnRefresh" : false,
"RefreshTokenExpiration" : 1,
"AccessTokenType" : 0,
"EnableLocalLogin" : true,
"IdentityProviderRestrictions" : [],
"IncludeJwtId" : false,
"Claims" : [],
"AlwaysSendClientClaims" : false,
"ClientClaimsPrefix" : "client
",
"PairWiseSubjectSalt" : null,
"AllowedCorsOrigins" : [],
"Properties" : {}
}

my example config in app.js :

export default class App extends Component<{}, State> {
auth = new AppAuth({
clientId: 'native.code',
issuer: 'https://192.168.180.51', // <-- LAN IP auth server
redirectUrl: 'io.identityserver.demo:/oauthredirect'
});

the error in the log-android :

E/ReactNativeJS(27986): { [Error: Failed to fetch configuration] framesToPop: 1, code: 'RNAppAuth Error' }

The picture taken on my device :

20171219 161851 HDR

Thanks !

Cannot read property 'authorize' of undefined

I get TypeError: Cannot read property 'authorize' of undefined with this setup

import { authorize as auth } from 'react-native-app-auth';

const config = {
  clientId: 'app-id',
  clientSecret: 'super-secret',
  redirectUrl: 'com.app://redirect',
  scopes: ['activity', 'sleep'],
  serviceConfiguration: {
    authorizationEndpoint: 'https://domain/authorize',
    tokenEndpoint: 'https://domain.tld/token',
    revocationEndpoint: 'https://domain.tld/revoke'
  }
};

auth(config).then(result => {
  console.log(result);
}).catch(e => {
  console.error(e);
});

Identity Server 3.0

Before I try on IdentityServer 3, do you know this supports 3.0 or not?

Thank you

Typescript definitions

I have created Typescript definitions, and wanted to check if you wanted to include this in the package or if I should simply add them to DefinitelyTyped.

declare module "react-native-app-auth" {
  interface IAppAuthProperties {
    scopes: string[];
    issuer: string;
    clientId: string;
    redirectUrl: string;
    aditionalParameters?: object;
  }

  function authorize(
    properties: IAppAuthProperties
  ): Promise<{
    accessToken: string;
    accessTokenExpirationDate: string;
    additionalParameters: object;
    idToken: string;
    refreshToken: string;
    tokenType: string;
  }>;

  function refresh(
    properties: IAppAuthProperties,
    { refreshToken: string }
  ): Promise<AuthorizeResult>;

  function revoke(
    properties: IAppAuthProperties,
    { tokenToRevoke: string, sendClientId: boolean }
  ): Promise<void>;
}

Trust anchor for certification path not found.

RNAppAuthModule > onActivityResult > onTokenRequestCompleted.

I can sign in and get the authorization token, but then when exchanging the token for an access token and refresh token I get javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

The certificate chain on the server returns Verify return code: 0 (ok), and has intermediate certs. It's set up fine. The Root and intermediate certs are installed on the phone emulator. What is wrong?

Support for login without using SSO IOS

I'm not sure whether this is supported by appauth-ios or not , but I'd like to sign in without using SSO and so remove the popup box that appears, and also share my id server cookie with my safari views., which we are currently breaking out into for certain bits of functionality which will not work (openid/AppAuth-iOS#120).

They suggest on to edit the code directly here

openid/AppAuth-iOS#182

I'm not an objective c developer though so any advice on how I'd be able to get this working with your library would be appreciated.

Feature request: Decoding id_token

Hi gents, I was working with auth again a little bit and found a possible improvement. It would be of reasonable value to add id_token decoding imho. It appears to be an easy task with a lot of potential value. There's apparently 2 libs to choose from https://jwt.io/#libraries-io. Oidc-client which is kind of web equivalent of what you have here is doing it. It's handy to be able to get user id, email, username and so on hassle free. What do you think?

Token Storage advice in Readme

A common question when implementing OAuth is how to store the secrets when the application is not running. Currently the readme is silent on this. I'd like to suggest a section that answers:

  • Does react-native-app-auth provide a secure token storage mechanism or is that left as an exercise for the developer?
  • If not, what tool / storage approach is recommended? react-native-keychain?

Authenticating with GitHub

Here's my config

{
  serviceConfiguration: {
    authorizationEndpoint: 'https://github.com/login/oauth/authorize',
    tokenEndpoint: 'https://github.com/login/oauth/access_token'
  },
  clientId: '',
  clientSecret: '',
  redirectUrl: 'github://oauth',
  scopes: ['gist']
}

When I call authorize, the GitHub login page opens, and once I click Authorize, it comes back to the app, and throws the following error

The operation couldn’t be completed. (org.openid.appauth.general error -7.)

<unknown>
    NativeModules.js:80:32
MessageQueue.__invokeCallback
    MessageQueue.js:400:4
<unknown>
    MessageQueue.js:139:11
MessageQueue.__guardSafe
    MessageQueue.js:316:6
MessageQueue.invokeCallbackAndReturnFlushedQueue
    MessageQueue.js:138:9
<unknown>
    debuggerWorker.js:72:58

What does that error mean? Doesn't give me much to go on.

Is it a config issue? Or something to do with redirectUrl?

Android: support for API 16 - 19

Related issue: #14

The current implementation supports Android API 21 (Lollipop) and above, however the parent library supports Android API 16 (Jellybean) and above.

This is because we are using the startActivityForResult and onActivityResult which do not work in earlier versions: when calling startActivityForResult, the callback to onActivityResult gets executed immediately with a null intent instead of waiting for the callback from the browser response.

According to this and this issue in the AppAuth-Android repo, the only way to fix this is to use performAuthorizationRequest(request, completionIntent, cancelIntent) directly instead of startActivityForResult.

Support custom authentication endpoints

We are currently relying on the openID discovery endpoint to infer the authorisation and revocation urls. Whereas this is very convenient most of the time, there can be cases where the discovery endpoint does not exist which makes it impossible to use this library.

It should therefore be possible to configure an auth session with custom url schemes rather than fetching from the discovery endpoint, which would make this library usable for services that are not openid-compliant.

assembleAndroidTest fails with "Unable to merge dex" error

I'm trying to run android unit tests after adding react-native-app-auth to my project,
and I get the following error:

Execution failed for task ':react-native-app-auth:transformDexArchiveWithExternalLibsDexMergerForDebugAndroidTest'.
> java.lang.RuntimeException: java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

I cloned the repo and tried to execute assembleAndroidTest in the provided example app, but it failed with the same error.

After doing some research, I discovered that removing implementation "net.openid:appauth:0.7.0" from the library's build.gradle makes the assembleAndroidTest task to pass.
I'm guessing it means that there is some dependency conflict or mismatch for test dependencies, but I couldn't figure out which.

Your help is appreciated.

More presets/examples

Would be nice to see sample configurations for more OAuth providers:

  • GitHub
  • Auth0
  • Slack
  • Microsoft Live
  • LinkedIn
  • Azure AD B2C (@imranolas?)
  • Uber
  • OneLogin
  • Steam
  • Facebook
  • Twitter

We could consider adding common social providers as well (Facebook, Twitter), although these might be better served by their own native SDKs to benefit from the app-based login, so perhaps just link to them instead.

We can pick up some of the configurations here: https://github.com/fullstackreact/react-native-oauth/blob/3063886c162758278a0f54fe423ba74730bd1d92/lib/authProviders.js#L23-L77

Some more ideas: https://github.com/danielctull/DCTAuth#oauth-20

Missing data intent in onActivityResult crashes app

No clue how users get here, unable to get there myself, but we've seen a good percentage of users crash:

net.openid.appauth.Preconditions.checkNotNull
Preconditions.java - line 55
java.lang.NullPointerException: dataIntent must not be null

net.openid.appauth.Preconditions.checkNotNull Preconditions.java:55
net.openid.appauth.AuthorizationResponse.fromIntent AuthorizationResponse.java:565
com.reactlibrary.RNAppAuthModule.onActivityResult RNAppAuthModule.java:204
com.facebook.react.bridge.ReactContext.onActivityResult ReactContext.java:255
com.facebook.react.ReactInstanceManager.onActivityResult ReactInstanceManager.java:706
com.facebook.react.ReactActivityDelegate.onActivityResult ReactActivityDelegate.java:126
com.facebook.react.ReactActivity.onActivityResult ReactActivity.java:77
android.app.Activity.dispatchActivityResult Activity.java:5444
android.app.ActivityThread.deliverResults ActivityThread.java:3404
android.app.ActivityThread.handleSendResult ActivityThread.java:3451
android.app.ActivityThread.access$1200 ActivityThread.java:154
android.app.ActivityThread$H.handleMessage ActivityThread.java:1332
android.os.Handler.dispatchMessage Handler.java:99
android.os.Looper.loop Looper.java:137
android.app.ActivityThread.main ActivityThread.java:5306
java.lang.reflect.Method.invokeNative Method.java
java.lang.reflect.Method.invoke Method.java:511
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run ZygoteInit.java:1102
com.android.internal.os.ZygoteInit.main ZygoteInit.java:869
dalvik.system.NativeStart.main NativeStart.java

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.