Supabase auth Facebook error: Error getting user email from external provider - supabase

I'm trying to test out my Nuxt app with Facebook login but I keep getting an error on the address bar:
?error=server_error&error_description=Error+getting+user+email+from+external+provider#_=_
No other errors on the console. I got all set up: Facebook app, Facebook advanced access on email and public_profile, callback URLs, app ID, and keys. It would go through Facebook's auth page then it redirects to my localhost with the error. User won't be registered to auth.users as well.
const supabase = useSupabaseAuthClient();
const socialAuth = async (provider) => {
const { error } = await supabase.auth.signInWithOAuth({ provider: provider });
if (error) {
console.log(error);
} else {
console.log("Social Auth success.");
}
};
<template>
<div>
<button #click="socialAuth('facebook')">Login with Facebook</button>
</div>
</template>
I've tried posting this in their GitHub discussions but I haven't received an answer from the community so far.

Related

supabase auth nextjs SSR without helpers

Im trying to implement a simple sign-up and sign-in with next.js and supabase. i can create the user with the signup method and i can sign in the user with the signInWithPassword method. for some reason i cant seem to set the cookie with the jwt payload. even though that is wat is returned by the sign in method.
I dont want to use the auth helpers as explained here. https://supabase.com/docs/guides/auth/auth-helpers/nextjs
i want to do the auth serverside with nextjs api routes. im using supabase/js-v2
here is what i have.
try {
const { data, error } = await supaClientAnon.auth.signUp({
email,
password
});
if (error) {
res.status(422).json({ message: error.message });
return;
}
const sess = await supaClientAnon.auth.setSession(data?.session as Session);
return res.status(201).json({ message: 'Signed up!', data, sess });
} catch (error) {
res.status(500).json({ message: 'Something went wrong' });
return;
}
session wont get set. the setSession method supposedly sets the sesison with the session data returned from the signUp method as explained here https://supabase.com/docs/reference/javascript/auth-setsession
am i misunderstanding supabase auth fundamentally or forgetting something else?

Nuxt Auth + socialite manual login

I am trying to use the Nuxt Auth module and socialite for social logins. I have an API in Laravel 8 and a client in NuxtJS.
I am wondering if someone could tell me:
If my logic here is ok?
How to manually log user with nuxt auth (manually set token and user)?
So currently here is my flow for social logins:
Click on login with google makes an API call to my backend where I get redirect URL from socialite to google.
My frontend redirects me to the google login page, then back to my frontend app after I picked the account.
I send the google code to the backend to callback endpoint for socialite which then grabs me the google user. In same place after I deal with users in my app I am returning the token which I create by doing return $this->okResponse(['token' => $nativeUser->createToken('social-login')->accessToken]);
At this point I am back in my front end with the token which then I am trying to set this.$auth.setUserToken(response.data.token) which apparently does not equal logging the user in. So it looks like this:
mounted() {
this.$axios.get(`login/social/google/callback`, {params: {code: this.$route.query.code}}).then(response => {
console.log(response)
// this.$auth.strategy.token.set(response.data.token)
this.$auth.setUserToken(response.data.token)
if (this.$auth.loggedIn) {
console.log('I am logged in!')
} else {
console.log('I am NOT logged in!', this.$auth)
}
})
}
I am not logged in. Do I have to manually set loggedIn, 'User' and token? Or this is just madness? The only other thing I see here as a solution is my own custom strategy but this seems like total overkill.
Not sure if this is the best way but I ended up doing something like this.
mounted() {
this.$axios.get(`login/social/google/callback`, {params: {code: this.$route.query.code}}).then(response => {
this.$auth.setUserToken(response.data.token)
this.$auth.setUser(response.data.user)
if (this.$auth.loggedIn) {
console.log('I am logged in!')
} else {
console.log('I am NOT logged in!', this.$auth)
}
})
}
This seems to be working ok.

Log-in users in flutter through social accounts with laravel-socialite backend

I am working on a flutter application, and I want to implement social login (Google and Facebook).
My API is implemented with Laravel and uses Laravel-socialite to authenticate users, there is the backend, web frontend (using VueJs) and now I am working on the mobile application using flutter.
The web application is working good (using the vue-social-auth package).
What I have done till now:
Used flutter_google_sign_in to handle authentication on the flutter app.
Did configure the package and I can successfully get user info through that package.
Problem I am facing:
What I don't seem to get working is to send the user that just logged in to the backend in order to provide an in-app user experience.
This is what the vue-social-auth package provides and what I send to the backend, which is working fine:
{code: "4/0AY0e-g442SMxdtLb_MVdQ63u1ydp48bbCRQco5Azoyf3y1rvYybDabyZGOvwAs7ZFJDQHA", scope: "email+profile+openid+https://www.googleapis.com/au…le+https://www.googleapis.com/auth/userinfo.email", authuser: "0", prompt: "consent"}
And this is what flutter_google_sign_in gives (aside of the user profile data:
idToken: "",
accessToken: "",
serverAuthCode: "",
serverAuthCode is always null.
How can I make it so that, using the same API logic, log-in users on flutter through social accounts?
Thank you.
Apparently, google sign in doesn't work on flutter except with Firebase/some cloud API backend service. I was using a local Laravel API for user auth so adding google sign in functionality requires setting up a firebase account/profile, downloading and adding the googleservices.json file to flutter project as explained in google_sign_in package installation manual. You also need to import firebase-auth package
Flutter Code (I use flutter modular pattern but same applies with Bloc/Provider if you get the idea as explained by Hamza Mogni above)
import 'package:google_sign_in/google_sign_in.dart';
import 'package:firebase_auth/firebase_auth.dart';
final GoogleSignIn _googleSignIn = GoogleSignIn();
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<LoginResponseModel> googleLoginResponse() async {
String url = env['API_BASE_URL'] + '/api/auth/google';
//click on google sign in. Get accessToken from google through googlesignin
plugin.
//Send accessToken to socialite in backend to request/create user data
GoogleSignInAccount googleSignInAccount = await _googleSignIn.signIn();
if (googleSignInAccount == null) {
print('Google Signin ERROR! googleAccount: null!');
return null;
}
GoogleSignInAuthentication googleSignInAuthentication =
await googleSignInAccount.authentication;
//this is user access token from google that is retrieved with the plugin
print("User Access Token: ${googleSignInAuthentication.accessToken}");
String accessToken = googleSignInAuthentication.accessToken;
//make http request to the laravel backend
final response =
await http.post(
url,
body: json.encode({"token": accessToken}),
headers: {"Content-Type": "application/json"});
if (response.statusCode == 200 || response.statusCode == 422) {
return LoginResponseModel.fromJson(
json.decode(response.body), // {'message':'Google signin successful'}
);
} else {
throw Exception('Failed to load data!');
}
}
For Logout function, you need to signout of both firebase and google account instance or you will always be logged in by the first known/used google account in subsequent login attempts.
Future<LogoutResponseModel> logout() async {
try {
await _auth.signOut();
await _googleSignIn.disconnect();
} catch (e) {
print('Failed to sign out ' + e.toString());
}
//api route to destroy sanctum token. santum token is added as authorization header
var url = env['API_BASE_URL'] + "/api/logout";
final response =
await http.post(Uri.tryParse(url), headers: {'Bearer ' $sanctumtoken});
if (response.statusCode == 200 || response.statusCode == 422) {
return LogoutResponseModel.fromJson(
json.decode(response.body),
);
} else {
throw Exception('Failed to load data!');
}
}
Laravel Code (route to controller method is api/auth/google, method expects to receive google access token from flutter app)
public function requestTokenGoogle(Request $request) {
// Getting the user from socialite using token from google
$user = Socialite::driver('google')->stateless()->userFromToken($request->token);
// Getting or creating user from db
$userFromDb = User::firstOrCreate(
['email' => $user->getEmail()],
[
'email_verified_at' => now(),
'first_name' => $user->offsetGet('given_name'),
'last_name' => $user->offsetGet('family_name'),
'avatar' => $user->getAvatar(),
]
);
// Returning response
$token = $userFromDb->createToken('Laravel Sanctum Client')->plainTextToken;
$response = ['token' => $token, 'message' => 'Google Login/Signup Successful'];
return response($response, 200);
}
I have solved it, after some digging I found out Laravel-Socialite has the functionality to log in users using their token built-in:
Quoting Socialite documentation:
If you already have a valid access token for a user, you can retrieve their details using Socialite's userFromToken method.

Azure/Msal authentication inside PowerApp Component Framework returns AADSTS50177 error

I created a simple PowerApps Component Framework using the pac pcf init command.
After successfully packaging and importing this skeleton PCF application to my demo tenant I tried to add MSAL authentication to it.
I used the #azure/msal npm package to write a typescript configuration and login without adding React or Angular npm packages. I only used #azure/msal and package added during the pcf create process.
The final goal was to use the token received from the msal authentication make a request on a authorized method in my Wep Api.
The problem is that my Web Api is not located in my demo tenant and the user that is used for msal authentication is from the demo tenant and does not exist on the tenant of my Web Api.
I cannot change the login user in the popup window as it only displays the error message, and the guest user that was added to the demo tenant, that has access to the Web API cannot have Certificates added to it through portal azure or portal office admin center pages.
This is my login configuration(I will omit the tenant names and client id for the work tenant):
import { AuthenticationParameters, Configuration, UserAgentApplication } from '#azure/msal';
import { AuthOptions, CacheOptions, FrameworkOptions } from "#azure/msal/lib-commonjs/Configuration";
public init(context: ComponentFramework.Context<IInputs>, notifyOutputChanged: () => void, state: ComponentFramework.Dictionary, container:HTMLDivElement)
{
// Add control initialization code
const auth: AuthOptions = {
clientId:'clientid',
authority:'https://login.microsoftonline.com/tenantid',
redirectUri:'redirect uri',
validateAuthority: true
};
const cache: CacheOptions = {
cacheLocation:"localStorage"
};
const framework: FrameworkOptions = {
protectedResourceMap: new Map([
['web api url',['https://tenantid/clientid/uniquename (scope)']],
['CRM work sandbox',['CRM work sandbox user impersonation permission(scope)']]
]),
unprotectedResources:[]
};
const config: Configuration = {
auth: auth,
cache: cache,
framework: framework
};
const params: AuthenticationParameters = {
authority: 'https://login.microsoftonline.com/tenantid',
scopes:['offline_access',
'https://tenantid/clientid/uniquename(scope)',
'CRM work sandbox user impersonation permission(scope)'],
redirectUri:'web api redirect uri'
};
const userAgentApplication = new UserAgentApplication(config);
const login = userAgentApplication.loginPopup(params).then(data => {
console.log(data);
let user = userAgentApplication.getAccount();
console.log(user);
if (user) {
// signin successful
console.log('success');
} else {
// signin failure
console.log('fail');
}
}, function (error: string) {
// handle error
console.log('Error' + error);
});
}
The error message displayed:
AADSTS50177: User account 'user name' from identity provider
'https://sts.windows.net/guid/' does not exist in tenant 'name'
and cannot access the application 'client id'(name of registered
app in portal azure) in that tenant. The account needs to be
added as an external user in the tenant first. Sign out and
sign in again with a different Azure Active Directory user account.
Is there a way to test this without adding the pcf or account in my work tenant ?

msal token timeout using a React SPA

I am using the msal library from npm (version 0.1.3) in a react application and can successfully redirect the user to login in and then acquire an access token. The issue I'm having is that after an hour the token will timeout and my API will return a 401.
I notice that when I refresh the SPA in the browser the msalRenewFrame iframe no longer appears in the html source even though I reinitialize the Msal.UserAgentApplication each time, however I'm not sure if that's the issue because the app times out even when I haven't refreshed.
Below is the code that runs each time the app is loaded:
const authCallback = (errorDesc: string, token: string, error: string, tokenType: string) => {
console.log('authCallback (errorDesc, token, error, tokenType)', errorDesc, token, error, tokenType);
if (error) {
console.error(error);
}
let scopes = [
process.env.REACT_APP_AZURE_SCOPE_URL + '/read',
process.env.REACT_APP_AZURE_SCOPE_URL + '/write'
];
msal.acquireTokenSilent(scopes)
.then(scopeApiToken => {
apiToken = scopeApiToken;
sessionStorage.setItem('apiToken', scopeApiToken);
renderApp();
})
.catch(e => {
console.error(e);
});
};
msal = new Msal.UserAgentApplication(
process.env.REACT_APP_AZURE_B2C_WEB_CLIENT_APPID!,
process.env.REACT_APP_AZURE_B2C_SIGNIN_URL!,
authCallback,
{
redirectUri: window.location.origin,
logger: new Msal.Logger((level: Msal.LogLevel, message: string, containsPii: boolean) => {
console.log(message);
})
}
);
let user = msal.getUser();
let isCallback = msal.isCallback(window.location.hash);
if (apiToken) {
renderApp();
} else if (user || isCallback) {
ReactDOM.render(
<div>
<Login
content={<Spinner size={SpinnerSize.medium} label="Signing in" />}
/>
</div>,
root);
} else {
ReactDOM.render(
<div>
<Login
redirectToRoot={true}
content={
<PrimaryButton onClick={() => msal.loginRedirect(['openid'])}>
Sign in with Microsoft
</PrimaryButton>}
/>
</div>,
root);
}
Well, after earning a tumbleweed badge for this post I figured out the answer was that I needed to call msal.acquireTokenSilent each time I wanted to call the external API. (I had been mistakenly thought the msal library would continuously update it silently in the background).
msal.acquireTokenSilent(scopes);

Resources