Advertisement
Guest User

Untitled

a guest
May 26th, 2018
194
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import React, { Component } from 'react';
  2. import PropTypes from 'prop-types';
  3. import * as uuid from 'uuid';
  4. import Toast from 'react-native-simple-toast';
  5. import { Dimensions, Modal, SafeAreaView, Text, TextInput, TouchableOpacity, View, WebView, Alert } from 'react-native';
  6. import { CONST } from './azureAD';
  7. import PreloaderScreen from '../../components/PreloaderScreen';
  8.  
  9. const loginUrl =
  10.   'https://login.microsoftonline.com/<tenant id>/oauth2/authorize';
  11.  
  12. export default class ADLoginView extends Component {
  13.   static propTypes = {
  14.     context: PropTypes.object.isRequired,
  15.     style: PropTypes.object.isRequired,
  16.     onSuccess: PropTypes.func.isRequired,
  17.     onBackpress: PropTypes.func.isRequired,
  18.     authority_host: PropTypes.string,
  19.     needLogout: PropTypes.bool,
  20.   };
  21.  
  22.   static defaultProps = {
  23.     authority_host: loginUrl,
  24.     needLogout: false,
  25.   };
  26.  
  27.   constructor(props) {
  28.     super(props);
  29.     this._handleADToken = this._handleADToken.bind(this);
  30.     const { context, needLogout } = props;
  31.     const { tenant } = context.getConfig();
  32.     this._needRedirect = needLogout || false;
  33.     this.state = {
  34.       page: this._getLoginUrl(tenant || 'common'),
  35.       visible: true,
  36.       color: '#000',
  37.       authenticating: false,
  38.       username: null,
  39.       password: null,
  40.     };
  41.     this._lock = false;
  42.   }
  43.  
  44.   componentWillReceiveProps(nextProps) {
  45.     if (!this.props.needLogout && nextProps.needLogout) {
  46.       const { context } = this.props;
  47.       const { tenant } = context.getConfig();
  48.       this._needRedirect = nextProps.needLogout || false;
  49.       this.setState({
  50.         page: this._getLoginUrl(tenant || 'common'),
  51.         visible: true,
  52.       });
  53.     }
  54.   }
  55.  
  56.   componentWillUpdate(nextProps, nextState) {
  57.     if (
  58.       this.state.visible === nextState.visible &&
  59.       this.state.page === nextState.page
  60.     ) {
  61.       return false;
  62.     }
  63.     return true;
  64.   }
  65.  
  66.   _needRedirect;
  67.   _onTokenGranted;
  68.   _lock;
  69.   _accessToken;
  70.  
  71.   /**
  72.    * Get authority host URI,
  73.    * @param {string} tenant Custom tenant ID, this filed is optional, default
  74.    *                        values is `common`.
  75.    * @return {string} The Authority host URI.
  76.    */
  77.   _getLoginUrl(tenant) {
  78.     const authUrl = String(this.props.authority_host || loginUrl).replace(
  79.       '<tenant id>',
  80.       tenant,
  81.     );
  82.     const context = this.props.context || null;
  83.     // eslint-disable-next-line
  84.     const { prompt, redirect_uri, client_id } = context.getConfig();
  85.  
  86.     if (context !== null) {
  87.       let result = [
  88.         `${authUrl}?response_type=code`,
  89.         `client_id=${client_id}`, // eslint-disable-line
  90.         `&nonce=rnad-${Date.now()}`,
  91.       ].join('&');
  92.  
  93.       if (this._needRedirect) {
  94.         result = `https://login.microsoftonline.com/${
  95.           this.props.context.getConfig().tenant
  96.         }/oauth2/logout?post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F:165`;
  97.       }
  98.  
  99.       return result;
  100.     }
  101.  
  102.     throw new Error('AD context should not be null/undefined.');
  103.   }
  104.  
  105.   /**
  106.    * An interceptor for handling webview url change, when it detects possible
  107.    * authorization code in url, it will triggers authentication flow.
  108.    * @param  {object} e Navigation state change event object.
  109.    */
  110.   _handleADToken(e) {
  111.     // eslint-disable-next-line
  112.     let code = /((\?|\&)code\=)[^\&]+/.exec(e.url);
  113.     const { authenticating } = this.state;
  114.     this.lastPage = e.url;
  115.  
  116.     const authFlow =
  117.       e.url.includes('sts.warnerbros.com') && !e.url.includes('@');
  118.  
  119.     console.log(e);
  120.  
  121.     if (authFlow && !e.loading && !authenticating) {
  122.       console.log('Starting auth flow');
  123.       const { username, password } = this.state;
  124.  
  125.       if (username && password) {
  126.         console.log('Already have credentials, redirecting');
  127.         let { url } = e;
  128.         if (url.indexOf('@') === -1) {
  129.           url = url.replace('://', `://${username}:${password}@`);
  130.           console.log('Setting URL to', url);
  131.           this.setState({ page: url });
  132.           this.webview.reload();
  133.         }
  134.       } else {
  135.         this.setState({ authenticating: true });
  136.       }
  137.     }
  138.  
  139.     if (this._needRedirect) {
  140.       return true;
  141.     }
  142.  
  143.     if (code !== null) {
  144.       this.setState(() => ({ visible: false, color: '#FFF' }));
  145.       // eslint-disable-next-line
  146.       code = String(code[0]).replace(/(\?|\&)?code\=/, "");
  147.       this._getResourceAccessToken(code).catch(err => {
  148.         this.setState(() => ({ color: 'red' }));
  149.         this.props.onBackpress();
  150.         Toast.show(err.message ? err.message : err);
  151.       });
  152.       return true;
  153.     }
  154.  
  155.     return true;
  156.   }
  157.  
  158.   /**
  159.    * Get access token for each resoureces
  160.    * @param {string} code The authorization code from `onNavigationStateChange`
  161.    *                      callback.
  162.    * @return {Promise<void>}
  163.    */
  164.   _getResourceAccessToken(code) {
  165.     const { context } = this.props;
  166.  
  167.     if (!context) {
  168.       throw new Error(
  169.         'property `context` of ADLoginView should not be null/undefined',
  170.       );
  171.     }
  172.  
  173.     const adConfig = this.props.context.getConfig();
  174.  
  175.     // eslint-disable-next-line
  176.     let { client_id, resources } = adConfig;
  177.     // Transform resource string to array
  178.     if (typeof resources === 'string') {
  179.       resources = [resources];
  180.     } else if (Array.isArray(resources)) {
  181.       resources = resources.length === 0 ? null : resources;
  182.     }
  183.  
  184.     let promises = [];
  185.     const config = { client_id, code, resource: 'common' };
  186.  
  187.     if (resources === null || resources === undefined) {
  188.       promises.push(
  189.         context.grantAccessToken(CONST.GRANT_TYPE.AUTHORIZATION_CODE, config),
  190.       );
  191.     } else {
  192.       promises = resources.map(rcs => {
  193.         const cfg = Object.assign({}, config, { resource: rcs });
  194.         return context.grantAccessToken(
  195.           CONST.GRANT_TYPE.AUTHORIZATION_CODE,
  196.           cfg,
  197.         );
  198.       });
  199.     }
  200.  
  201.     return Promise.all(promises)
  202.       .then(() => {
  203.         if (!this.props.context) {
  204.           throw new Error(
  205.             'value of property `context` is invalid=',
  206.             this.props.context,
  207.           );
  208.         }
  209.  
  210.         // trigger loggined finished event
  211.         if (this.props.context !== null) {
  212.           this.setState(() => ({ color: '#FFF' }));
  213.           this.props.onSuccess(context.getCredentials());
  214.         }
  215.       })
  216.       .catch(err => {
  217.         throw new Error(err);
  218.       });
  219.   }
  220.  
  221.   render() {
  222.     // Fix visibility problem on Android webview
  223.     const js = `document.getElementsByTagName('body')[0].style.height = '${
  224.      Dimensions.get('window').height
  225.    }px';`;
  226.  
  227.     if (!this.state.visible) {
  228.       return (
  229.         <PreloaderScreen
  230.           color={this.state.color}
  231.           key={uuid.v4()}
  232.           style={{
  233.             position: 'absolute',
  234.             left: 0,
  235.             right: 0,
  236.             bottom: 0,
  237.             top: 0,
  238.           }}
  239.         />
  240.       );
  241.     }
  242.  
  243.     return (
  244.       <SafeAreaView
  245.         style={{
  246.           flex: 2,
  247.           width: '100%',
  248.           backgroundColor: '#262626',
  249.         }}
  250.       >
  251.         <View style={{ flex: 1 }}>
  252.           <WebView
  253.             automaticallyAdjustContentInsets={false}
  254.             ref={ref => (this.webview = ref)}
  255.             style={[
  256.               this.props.style,
  257.               {
  258.                 flex: 1,
  259.                 alignSelf: 'stretch',
  260.                 width: Dimensions.get('window').width,
  261.                 height: Dimensions.get('window').height,
  262.               },
  263.             ]}
  264.             source={{ uri: this.state.page }}
  265.             javaScriptEnabled
  266.             domStorageEnabled
  267.             onLoadEnd={() => {
  268.               if (this._needRedirect) {
  269.                 this._needRedirect = false;
  270.                 const tenant =
  271.                   this.props.context.getConfig().tenant || 'common';
  272.                 this.setState({ page: this._getLoginUrl(tenant) });
  273.               }
  274.             }}
  275.             decelerationRate="normal"
  276.             javaScriptEnabledAndroid
  277.             onNavigationStateChange={this._handleADToken}
  278.             onShouldStartLoadWithRequest={() => true}
  279.             startInLoadingState
  280.             injectedJavaScript={js}
  281.             scalesPageToFit
  282.             onError={() => this.props.onBackpress()}
  283.           />
  284.           <Modal
  285.             animationType="slide"
  286.             transparent={false}
  287.             visible={this.state.authenticating}
  288.           >
  289.             <View style={{ flex: 1, marginTop: 22, padding: 50 }}>
  290.               <Text
  291.                 style={{
  292.                   fontSize: 25,
  293.                   color: 'black',
  294.                   textAlign: 'center',
  295.                   marginBottom: 22,
  296.                 }}
  297.               >
  298.                 Login again lol
  299.               </Text>
  300.  
  301.               <TextInput
  302.                 style={{ height: 50 }}
  303.                 placeholder="Username"
  304.                 onChangeText={username => this.setState({ username })}
  305.                 value={this.state.username}
  306.               />
  307.  
  308.               <TextInput
  309.                 style={{ height: 50 }}
  310.                 placeholder="Password"
  311.                 secureTextEntry
  312.                 onChangeText={password => this.setState({ password })}
  313.                 value={this.state.password}
  314.               />
  315.  
  316.               <TouchableOpacity
  317.                 onPress={() => {
  318.                   this.setState({ authenticating: false });
  319.                   console.log('Starting injecting JS');
  320.  
  321.                   const { username, password } = this.state;
  322.  
  323.                   let url = this.lastPage;
  324.                   if (url.indexOf('@') === -1) {
  325.                     url = url.replace('://', `://${username}:${password}@`);
  326.                   }
  327.  
  328.                   console.log('Setting URL to', url);
  329.  
  330.                   this.setState({ page: url });
  331.                   this.webview.reload();
  332.                 }}
  333.               >
  334.                 <Text
  335.                   style={{
  336.                     fontSize: 25,
  337.                     color: 'black',
  338.                     textAlign: 'center',
  339.                     padding: 20,
  340.                   }}
  341.                 >
  342.                   SUBMIT
  343.                 </Text>
  344.               </TouchableOpacity>
  345.             </View>
  346.           </Modal>
  347.         </View>
  348.       </SafeAreaView>
  349.     );
  350.   }
  351. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement