Skip to content

Unable to navigate away from Auth Component when Android OS destroys Activity #360

Closed as not planned
@AlteaDown

Description

@AlteaDown

Issue

The library can't seem to handle cases where Activity.onDestroy() is called when authenticating. Authentication is performed in a WebView separate from the Android Activity that is running RN, so that means when we move to it, the RN Activity is put on the backstack, and Android is now free to have the Activity.onDestroy() called on the Activity.

When Activity.onDestroy() is called on the RN Activity, and once authentication succeeds, the library attempts to launch the previous activity again (but it was destroyed so it needs to be recreated). So we end up with a new Activity/Component that is completely unrelated to the authorize call we originally made.

Some time after the new activity is done loading, our original call to authorize() will get a response from the token endpoint. However, the Component is detached, and unable to handle rendering. This means that if you want to navigate to a new Component, such as the rest of your app, you can not. The only way I can imagine being able to handle this is, first being aware that this situation can arise, when it does arise, and then to actually know that the state has changed on the new Component, you would have to use a reactive structure like a RxJS Subject that will emit to the new Component to allow it to handle this case, when it occurs.

You can recreate this scenario easily by using the "Don't Keep Activities" setting in your android Device's Developer Settings, or by using a low performance/low memory Android device.

Example code

import React from 'react'
import {Button, Text} from 'native-base'
import {Image, ImageStyle, StyleSheet, View, ViewStyle} from 'react-native'
import {NavigationParams, NavigationScreenProps} from 'react-navigation'
import {authorize} from 'react-native-app-auth'
import Config from 'react-native-config'

type State = {
  showLogin: Boolean,
}

export default class LoginComponent extends React.Component<NavigationScreenProps, State> {
  constructor(props: Readonly<NavigationScreenProps<NavigationParams, State>>) {
    super(props)

    this.state = {
      showLogin: true,
    }
  }

  async authWithBackend() {
    this.setState({
      showLogin: false,
    })

    // use the client to make the auth request and receive the authState
    try {
      const authorizationResponse: any = await authorize({
        clientId: 'f3d259ddd3ed8ff3843839b',
        clientSecret: '4c7f6f8fa93d59c45502c0ae8c4a95b',
        redirectUrl: 'io.viamo.clipboard://',
        scopes: [],
        serviceConfiguration: {
          authorizationEndpoint: Config.OAUTH_ISSUER_URL,
          tokenEndpoint: Config.OAUTH_TOKEN_URL,
        },
        usePKCE: false,
      })

      this.props.navigation.navigate('MainComponent')
    } catch (error) {
      console.error(error)
    }
  }

  private loginButton() {
    if (this.state.showLogin) {
      return (
        <Button style={styles.signInButton} onPress={() => this.authWithBackend()}>
          <Text>
            Sign In
          </Text>
        </Button>
      )
    } else {
      return null
    }
  }

  render() {
    return (
      <View style={styles.root}>
        <Image source={{uri: 'viamo_logo'}} style={styles.logo}/>
        {this.loginButton()}
      </View>
    )
  }

  // noinspection JSUnusedGlobalSymbols Used by NavigationController
  static navigationOptions = {
    header: null,
  }
}

interface IStyles {
  root: ViewStyle,
  signInButton: ViewStyle,
  logo: ImageStyle;
}

const styles = StyleSheet.create<IStyles>({
  root: {
    flex: 1,
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'space-evenly',
  },
  signInButton: {
    alignSelf: 'center',
    paddingLeft: 32,
    paddingRight: 32,
  },
  logo: {
    width: '85%',

    // Without height undefined it won't work :( (https://stackoverflow.com/a/53482563)
    height: undefined,

    // figure out your image aspect ratio
    aspectRatio: 488 / 171,
  },
})

Environment

  • Your Identity Provider: `e.g. Custom/Viamo.io
  • Platform that you're experiencing the issue on: Android
  • Are you using Expo?: No

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions