Skip to content

Instantly share code, notes, and snippets.

@harismh
Created September 22, 2021 00:55
Show Gist options
  • Save harismh/e823422f0dbc310b2d77da35b089d3ea to your computer and use it in GitHub Desktop.
Save harismh/e823422f0dbc310b2d77da35b089d3ea to your computer and use it in GitHub Desktop.
// @flow
import React from "react";
import * as WebBrowser from "expo-web-browser";
import Constants from "expo-constants";
import {
makeRedirectUri,
useAuthRequest,
useAutoDiscovery
} from "expo-auth-session";
import {
MicrosoftIconButton,
MicrosoftRectangleButton
} from "../../common/MicrosoftButton";
import { isValidOAuthToken } from "../../util/auth";
import type { OAuthConfig, OAuthToken } from "../../model/types";
import { Platform } from "react-native";
type Props = {
config: $NonMaybeType<$PropertyType<OAuthConfig, "azure">>,
iconStyle?: "rectangle" | "round",
onSuccess: OAuthToken => Promise<void>,
onError: ({ message: string, response: Object }) => void
};
// auth window won't close without this line
// https://docs.expo.io/guides/authentication/
WebBrowser.maybeCompleteAuthSession();
export default function AzureLogin({
config,
onSuccess,
onError,
iconStyle
}: Props) {
const {
tenantId,
clientId,
clientSecret,
scopes = ["openid", "profile", "offline_access"]
} = config;
if (!tenantId || !clientId || !clientSecret) {
throw new Error("Missing/malformed azure config");
}
const isDev =
Constants.manifest.packagerOpts && Constants.manifest.packagerOpts.dev
? true
: false;
const scheme =
Platform.OS === "android" && Constants.manifest.android && !isDev
? Constants.manifest.android.package
: Constants.manifest.scheme
? Constants.manifest.scheme
: "flip";
// const useProxy = Platform.select({ web: false, default: !isDev });
const discovery = useAutoDiscovery(
`https://login.microsoftonline.com/${tenantId}`
);
const redirectUri = makeRedirectUri({
// useProxy: true,
scheme,
path: "/oauth-redirect",
preferLocalhost: isDev,
isTripleSlashed: true
});
// eslint-disable-next-line no-unused-vars
const [authRequest, _, promptAsync] = useAuthRequest(
{
clientId,
scopes,
redirectUri
},
discovery
);
const onPress = async () => {
const authResp = await promptAsync();
if (authResp.type === "success") {
const authCode = authResp.params.code;
const params = {
client_id: clientId,
client_secret: clientSecret,
code_verifier: authRequest.codeVerifier,
code: authCode,
grant_type: "authorization_code",
redirect_uri: redirectUri
};
const body = Object.entries(params)
// $FlowFixMe Object.entries always returns mixed; strings are guaranteed here though
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join("&");
return fetch(discovery.tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
Origin: null
},
body
})
.then(resp => resp.json())
.then(respJson =>
isValidOAuthToken(respJson)
? onSuccess(respJson)
: onError({ message: "Malformed token", response: respJson })
)
.catch(err => {
console.error(err);
onError({
message: "Error fetching authentication token",
response: {}
});
});
} else if (authResp.params && "error" in authResp.params) {
return onError({ message: authResp.params.error, response: authResp });
} else {
return onError({
message: "Authorization session cancelled",
response: authResp
});
}
};
const MicrosoftIcon =
iconStyle === "round" ? MicrosoftIconButton : MicrosoftRectangleButton;
return <MicrosoftIcon disabled={!authRequest} onPress={onPress} />;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment