Advertisement
Guest User

Untitled

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