I'm using aws amplify along with react-native-inappbrowser. I enabled Google SSO authentication and it is working fine, but when I added GitHub auth it doesn't work as expected.
I added a listener using Hub.listen('auth', async (data) => {...} and the urlOpener in my awsConfig looks like this:
onst urlOpener = async (url, redirectUrl, settings) => {
try {
if (await InAppBrowser.isAvailable()) {
const { type, url: newUrl } = await InAppBrowser.openAuth(
url,
redirectUrl,
{ ...inAppBrowserSettings, ...settings },
);
if (type === 'success') {
Linking.openURL(newUrl);
}
} else {
Linking.openURL(url);
}
} catch (error) {
Alert.alert(error.message);
}
};
The Hub.listener looks like this:
useEffect(() => {
const unsubscribe = Hub.listen('auth', async (data) => {
const { payload } = data;
const user = payload.data;
switch (payload.event) {
case AuthEnum.SIGN_IN:
handleSignIn(user);
break;
case AuthEnum.SIGN_OUT:
handleSignOut();
break;
case AuthEnum.SIGN_IN_FAILURE:
handleSignInFailure();
break;
}
});
return () => unsubscribe();
}, []);
When I try to authenticate using GitHub, the GitHub API returns the correct token but the aws Hub catches a SIGN_IN_FAILURE and the data looks like this:
{
"channel": "auth",
"payload": {
"event": "signIn_failure",
"data": {},
"message": "The OAuth response flow failed"
},
"source": "Auth",
"patternInfo": []
}
I'm not sure why this is happening. A solution could be to remove the listener in case of GitHub auth, but I'm not sure how to do that since the InAppBrowser.openAuth() is used for both the Google SSO and GitHub auth.
Make sure to let me know if the info I provided isn't enough. Any suggestions/help would be great!
I don't think AWS Amplify support github oauth. I believe the supported ones are: Google, Apple, Facebook & Amazon
Supported social logins
Related
I am new to SvelteKit and i am trying to use MSAL.js with SvelteKit, the issue is i want to implement something similar to an AuthGuard/HttpInterceptor which checks to see if the user is still logged in as they navigate around the SPA or call the external API.
I am using the OAuth 2.0 authorization code flow in Azure Active Directory B2C
within in my auth.ts file i have the following code
let accountId: string = "";
const signIn = () => {
try {
msalInstance.loginRedirect(loginRequestWithApiReadWrite);
} catch (error) {
isAuthenticated.set(false);
console.warn(error);
}
}
// This captures the response from using the redirect flow to login
await msalInstance.handleRedirectPromise()
.then(response => {
if (response) {
if (response.idTokenClaims['tfp'].toUpperCase() === b2cPolicies.names.signUpSignIn.toUpperCase()) {
handleResponse(response);
}
}
})
.catch(error => {
console.log(error);
});
async function handleResponse(response: msal.AuthenticationResult) {
if (response !== null) {
user.set(response);
isAuthenticated.set(true);
setAccount(response.account);
} else {
selectAccount();
}
}
function selectAccount() {
const currentAccounts = msalInstance.getAllAccounts();
if (currentAccounts.length < 1) {
return;
} else if (currentAccounts.length > 1) {
const accounts = currentAccounts.filter(account =>
account.homeAccountId.toUpperCase().includes(b2cPolicies.names.signUpSignIn.toUpperCase())
&&
account.idTokenClaims?.iss?.toUpperCase().includes(b2cPolicies.authorityDomain.toUpperCase())
&&
account.idTokenClaims.aud === msalConfig.auth.clientId
);
if (accounts.length > 1) {
if (accounts.every(account => account.localAccountId === accounts[0].localAccountId)) { console.log("Multiple accounts belonging to the user detected. Selecting the first one.");
setAccount(accounts[0]);
} else {
console.log("Multiple users accounts detected. Logout all to be safe.");
signOut();
};
} else if (accounts.length === 1) {
setAccount(accounts[0]);
}
} else if (currentAccounts.length === 1) {
setAccount(currentAccounts[0]);
}
}
// in case of page refresh
selectAccount();
function setAccount(account: msal.AccountInfo | null) {
if (account) {
accountId = account.homeAccountId;
}
}
const authMethods = {
signIn,
getTokenRedirect
}
In a +page.svelte file i can then import the authMethods no problem, MSAL redirects me to the microsoft sign in page, i get redirected back and can then request an access token and call external API, great all is well.
<script lang='ts'>
import authMethods from '$lib/azure/auth';
<script>
<button on:click={authMethods.signIn}>Sign In</button>
However, the issue i am having is trying to implement this so i can check to see if the user is logged in against Azure B2C using a hook.server.ts file automatically. I would like to check a variable to see if the user is authenticated and if they arnt the hooks.server will redirect them to signUp by calling the authMethod within the hook, and the user will be automatically redirected to the sign in page.
In the hooks.server.ts i have the following code:
export const handle: Handle = (async ({ event, resolve }) => {
if (isAuthenticated === false) {
authRedirect.signIn();
msalInstance.handleRedirectPromise().then((response) => {
if (response) {
console.log('login with redirect succeeded: ', response)
isAuthenticated = true;
}
}).catch((error) => {
console.log('login with redirect failed: ', error)
})
}
const response = await resolve(event);
return response;
}) satisfies Handle;
When i navigate around the SvelteKit SPA, MSAL.js keeps throwing the error below, which i know is because i am running the code from the server flow rather than in the browser, so it was my understanding that if i implement the handleRedirectPromise() in both the auth.ts file and hooks.server.ts this would await the response from the signIn event and so long as i got a response i can then set isAuthenticated to true.
errorCode: 'non_browser_environment',
errorMessage: 'Login and token requests are not supported in non-browser environments.',
subError: ''
Are you required to use the MSAL library? I have got it working with https://authjs.dev/. I was using Active Directory -https://authjs.dev/reference/oauth-providers/azure-ad but there is also a flow for B2C https://authjs.dev/reference/oauth-providers/azure-ad-b2c which I haven't tried.
Then in the hooks.server.js you can do something like the below.
import { sequence } from '#sveltejs/kit/hooks';
import { redirect } from '#sveltejs/kit';
import { SvelteKitAuth } from '#auth/sveltekit';
import AzureADProvider from '#auth/core/providers/azure-ad';
import {
AZURE_AD_CLIENT_ID,
AZURE_AD_CLIENT_SECRET,
AZURE_AD_TENANT_ID
} from '$env/static/private'
const handleAuth = SvelteKitAuth({
providers: [
AzureADProvider({
clientId: AZURE_AD_CLIENT_ID,
clientSecret: AZURE_AD_CLIENT_SECRET,
tenantId: AZURE_AD_TENANT_ID
})
]
});
async function isAuthenticatedUser({ event, resolve }) {
const session = await event.locals.getSession();
if (!session?.user && event.url.pathname !== '/') {
throw redirect(302, '/');
} else if (session?.user && event.url.pathname === '/') {
throw redirect(302, '/dashboard');
}
const response = await resolve(event);
return response;
}
export const handle = sequence(handleAuth, isAuthenticatedUser);
I am currently trying to send FCM using Firebase Cloud Function in the Flutter WEB environment. But it doesn't work very well
My Problem
My index.js Code
/* eslint-disable max-len */
const admin = require("firebase-admin");
const {applicationDefault} = require("firebase-admin/app");
const functions = require("firebase-functions");
const cors = require("cors")({origin: true});
admin.initializeApp({credential: applicationDefault(), databaseURL: "https://<PROJECT-ID>.firebaseio.com"});
const messaging = admin.messaging();
exports.sendNoti = functions.runWith({
allowInvalidAppCheckToken: true}).https.onRequest(async (req, res) => {
cors(req, res, async () =>{
if (res.app == undefined) {
console.log("App Check failed-precondition / The function must be called from an App Check verified app.");
}
console.log(res.app);
try {
await messaging.sendToDevice(req.body.data.targetDevices, {
notification: {
title: req.body.data.messageTitle,
body: req.body.data.messageBody,
},
});
res.set("Access-Control-Allow-Origin", "*");
res.status(200).send({"status": "success", "data": "GOOD"});
return true;
} catch (ex) {
console.log(ex);
res.set("Access-Control-Allow-Origin", "*");
res.status(200).send({"status": "fail", "data": ex});
return ex;
}
});
});
Error Message
{errorInfo: {code: messaging/authentication-error, message: An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions. See https://firebase.google.com/docs/admin/setup for setup instructions. Raw server response: "HTML CODE
". Status code: 401.}, codePrefix: messaging}
My Try
credential: admin.credential.applicationDefault(),
credential: admin.credential.cert("service.json"),
Use OnCall function
Use AppCheck
I am trying to follow this tutorial here: https://github.com/Azure-Samples/ms-identity-javascript-nodejs-tutorial/tree/main/2-Authorization/1-call-graph. It uses the microsoft-identity-express wrapper for msal-node, but I am having trouble with some Graph routes.
I have set up my Azure AD app permissions exactly as described in the tutorial.
I have successfully got the /profile route working, like this:
router.get('/profile',
msid.isAuthenticated(),
msid.getToken({
resource: appSettings.protectedResources.graphAPI
}),
async (req,res,next) => {
let output;
try {
const graphClient = graphManager.getAuthenticatedClient(req.session.protectedResources["graphAPI"].accessToken);
output = await graphClient
.api('/me')
.get();
} catch (error) {
console.log(error);
next(error);
}
res.send(output);
}
);
My appSettings look like this:
const appSettings = {
appCredentials: {
clientId: "zzz",
tenantId: "yyy",
clientSecret: "xxx"
},
authRoutes: {
redirect: "/redirect",
error: "/error", // the wrapper will redirect to this route in case of any error
unauthorized: "/unauthorized" // the wrapper will redirect to this route in case of unauthorized access attempt
},
protectedResources: {
graphAPI: {
endpoint: "https://graph.microsoft.com/v1.0/me",
scopes: ["user.read"]
},
armAPI: {
endpoint: "https://management.azure.com/tenants?api-version=2020-01-01",
scopes: ["https://management.azure.com/user_impersonation"]
},
joinedTeams: {
endpoint: "https://graph.microsoft.com/v1.0/me/joinedTeams",
scopes: ["user.read", "teamsettings.readwrite.all"]
},
myEvents: {
endpoint: "https://graph.microsoft.com/v1.0/me/events",
scopes: ["user.read", "calendars.readwrite"]
},
messages: {
endpoint: "https://graph.microsoft.com/v1.0/me/messages",
scopes: ["Mail.ReadWrite"]
}
}
};
And finally, my getAuthenticatedClient function from graphManager is:
const getAuthenticatedClient = (accessToken) => {
// Initialize Graph client
const client = graph.Client.init({
// Use the provided access token to authenticate requests
authProvider: (done) => {
done(null, accessToken);
}
});
return client;
};
graphManager and appSettings are set up exactly how the tutorial does it. I added more API endpoints to the protectedResources object as the tutorial says. I followed the same logic process as the /profile route for the extra Graph API routes. But I still get 401 errors whenever running this:
router.get('/me/events',
msid.isAuthenticated(),
msid.getToken({
resource: appSettings.protectedResources.myEvents
}),
async (req,res,next) => {
let output;
try {
const graphClient = graphManager.getAuthenticatedClient(req.session.protectedResources["myEvents"].accessToken);
output = await graphClient
.api('/me/events')
.get();
} catch (error) {
console.log(error);
next(error);
}
res.send(output);
}
);
I can't understand why /me calls to the Graph API work but /me/events does not. The correct permissions are being passed to msid.getToken() and getAuthenticatedClient(), so why do the API calls fail?
I have a SPA which uses the solution provided here to authenticate with Azure AD and everything works as expected. Now I want to migrate this to use MSAL.js.
I use below for login:
import * as MSAL from 'msal'
...
const config = {
auth: {
tenantId: '<mytenant>.com',
clientId: '<myclientid>',
redirectUri: <redirecturi>,
},
cache: {
cacheLocation: 'localStorage',
}
};
const tokenRequest = {
scopes: ["User.Read"]
};
export default {
userAgentApplication: null,
/**
* #return {Promise}
*/
initialize() {
let redirectUri = config.auth.redirectUri;
// create UserAgentApplication instance
this.userAgentApplication = new MSAL.UserAgentApplication(
config.auth.clientId,
'',
() => {
// callback for login redirect
},
{
redirectUri
}
);
// return promise
return new Promise((resolve, reject) => {
if (this.userAgentApplication.isCallback(window.location.hash) || window.self !== window.top) {
// redirect to the location specified in the url params.
}
else {
// try pull the user out of local storage
let user = this.userAgentApplication.getUser();
if (user) {
resolve();
}
else {
// no user at all - go sign in.
this.signIn();
}
}
});
},
signIn() {
this.userAgentApplication.loginRedirect(tokenRequest.scopes);
},
And then I use below to get the token:
getCachedToken() {
var token = this.userAgentApplication.acquireTokenSilent(tokenRequest.scopes);
return token;
}
isAuthenticated() {
// getCachedToken will only return a valid, non-expired token.
var user = this.userAgentApplication.getUser();
if (user) {
// get token
this.getCachedToken()
.then(token => {
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
// get current user email
axios
.get('<azureapi-endpoint>' + '/GetCurrentUserEmail')
.then(response => { })
.catch(err => { })
.finally(() => {
});
})
.catch(err => { })
.finally(() => { });
return true;
}
else {
return false;
}
},
}
but after login I get below error:
Access to XMLHttpRequest at 'https://login.windows.net/common/oauth2/authorize?response_type=code+id_token&redirect_uri=<encoded-stuff>' (redirected from '<my-azure-api-endpoint>') from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Also the token that I get seems to be invalid as I get 401 errors trying to call api using the token. Upon checking the token against https://jwt.io/ I get an invalid signature.
I really appreciate anyone's input as I've already spent good few days and haven't got anywhere yet.
I'm not sure if this is your issue. however, for msal.js, in the config, there is no tenantId parameter, it's supposed to be authority. Here is a sample for graph api using msal.js
https://github.com/Azure-Samples/active-directory-javascript-graphapi-v2
specifically: the config is here: https://github.com/Azure-Samples/active-directory-javascript-graphapi-v2/blob/quickstart/JavaScriptSPA/authConfig.js
as per here, https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-js-initializing-client-applications it is supposed to be hitting login.microsoftonline.com not login.windows.net
I am trying to setup native Google authentication on react-native using Expo GoogleSignIn API, but can't get idToken for authorized Google User.
It has accessToken in response, but not idToken.
So I am using some straigtforward code like
const result = await GoogleSignIn.initAsync({
isOfflineEnabled: true,
isPromptEnabled: true,
//webClientId: 'webClientId',
clientId // now testing only on Android, so this is irrelevant
});
console.log(result);
Example response:
Object {
"auth": Object {
"accessToken": "accessToken",
"accessTokenExpirationDate": null,
"idToken": null, // here is the problem!
"refreshToken": null,
},
"displayName": "Danila Tsvetkov",
"email": "email#email",
"firstName": "Danila",
"hostedDomain": undefined,
"lastName": "Tsvetkov",
"serverAuthCode": null,
"uid": "uid",
}
At the same time Google OAuth API returns not only accessToken, but also idToken and refreshToken.
Everything else works fine, like authorization and sign in flow.
Maybe the problem is with serverAuthCode?
Tried to put webClientId, api stops working properly. Added SHA1 to google-services (more info), didn't help. Changing other params like "isOfflineEnabled" also doesn't do much.
Can you use this
const result = await Google.logInAsync({
androidClientId: "android id"
iosClientId: 'Ios Id',
scopes: ['profile', 'email'],
});
then
if (result.type === 'success') {
console.log(result.idToken);
//or
Alert.alert(result.idToken);
}
if (result.type === 'success') {
console.log(result.user.auth);
}
this is because we are using google-sign-in, instead of expo-google-app-auth
so in conclusion, use result.user.auth, but before that, your code should look something like this
signInAsync = async () => {
try {
await GoogleSignIn.askForPlayServicesAsync();
const result = await GoogleSignIn.signInAsync();
if (result.type === 'success') {
this.onSignIn(result.user.auth);
return result.user.auth;
}
} catch ({ message }) {
alert('login: Error:' + message);
}
};