Laravel Socialite Github provider email is null - laravel

I'm trying to setup login in Laravel with GitHub using Socialite.
Here is my provider callback function:
public function handleProviderCallback($provider)
{
$provider_user = Socialite::driver($provider)->user();
dd($provider_user);
if($provider_user->email) {
$user = User::firstOrCreate([
'email' => $provider_user->email
], [
'name' => $provider_user->name ?? $provider_user->nickname,
'password' => Hash::make(Str::random(16)),
]);
Auth::login($user, true);
}
return redirect('/');
}
When I dd the user that provider returns, I can see that values $provider_user->name and $provider_user->nickname are correct, but $provider_user->email is null.
Which, according to the GitHub API documentation shouldn't be the case, as my e-mail is public:
I have tried https://api.github.com/user/emails where I've tried putting user nickname, as well as client ID in place of user, but to no avail. The response is:
{ "message": "Not Found", "documentation_url": "https://docs.github.com/rest" }
So, am I doing something wrong or GitHub API simply no longer returns e-mail addresses? Is there a way to get a public e-mail address?

I was having the same problem earlier.
What I did upon creating the github app is change the User permissions->Email addresses to read only. You can update it on the permission & events:

Related

Create session on consuming login with api on laravel

I have an api that has a method to start and I am calling it from a frontend project.
In the front end project I use Guzzle to make the call via post to the api and login, from which I get back a json with the user data and a jwt token.
But when I receive the token as I manage the session, I must create a session and save the token, since the laravel to authenticate I need a model user and have a database, which of course I do not have in this backend because I call the api to log in, which brings a token and user data, then as I manage it from the backend, I'm a little lost there.
$api = new Api();
$response = $api->loginapi(['user'=>'wings#test.com','password'=>'123']);
Because here I could not do Auth::login($user) to generate the session.
Because I don't have here the database because the login is done from the api.
There I call the api, of which the answer is the token, but how do I manage it from here, creating a session? saving the token?
thanks for your help.
With api, you don't usually manage a session. usually, you'd call something like
Auth::attempt([
'email' => 'me#example.com',
'password' => 'myPassword'
]);
If the credentials are correct, laravel will include a Set-Cookie header in response, and, that is how you authenticate with api. Via an auth cookie. You don't need to do anything else.
Let's show you how:
//AuthController.php
public function login(Request $request) {
$validatedData = $request->validate([
'email' => 'required|email',
'password' => 'required'
]);
if(Auth::attempt($validatedData)){
return ['success' => 'true'];
}
else{
return ['success' => false, 'message' => 'Email or password Invalid'];
}
}
public function currentUser (){
return Auth::user();
}
Now, the APi file
Route::post('/login', ['App\Http\Controllers\AuthController', 'login']);
Route::get('/current_user', ['App\Http\Controllers\AuthController', 'currentUser']);
Now if you make a call to /api/current_user initially, you'll get null response since you're not currently logged in. But once you make request to /api/login and you get a successful response, you are now logged in. Now if you go to /api/current_user, you should see that you're already logged in.
Important ::
If you are using fetch, you need to include credentials if you're using something other than fetch, check out how to use credentials with that library or api
You want to use the API to authenticate and then use the SessionGuard to create session including the remember_me handling.
This is the default login controller endpoint for logging in. You don't want to change this, as it makes sure that user's do not have endless login attempts (protects for brut-force attacks) and redirects to your current location.
public function login(Request $request)
{
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
if ($request->hasSession()) {
$request->session()->put('auth.password_confirmed_at', time());
}
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
The core happens when we try to "attemptLogin" at
protected function attemptLogin(Request $request)
{
return $this->guard()->attempt(
$this->credentials($request), $request->boolean('remember')
);
}
When using the SessioGurad (which is default) the method attemptLogin fires a couple of events, checks if the user has valid credentials (by hashing the password and matching it with db) and then logs the user in, including the remember me functionality.
Now, if you don't care about events, you can just check from your API if the credentials match and then use the login method from the guard. This will also handle the remember me functionality. Something like this:
protected function attemptLogin(Request $request)
{
$username = $request->input($this->username());
$password = $request->input('password');
$result = \Illuminate\Support\Facades\Http::post(env('YOUR_API_DOMAIN') . '/api/v0/login' , [
'username' => $username,
'password' => $password
])->json();
if(empty($result['success'])){
return false;
}
// Maybe you need to create the user here if the login is for the first time?
$user = User::where('username', '=', $username)->first();
$this->guard()->login(
$user, $request->boolean('remember')
);
return true;
}

"Trying to access array offset on value of type null" when using laravel Socialite with function "userFromToken($token)"

I'm using laravel 8. I'm trying to implement sign in with google option for a mobile application and developing API using laravel Socialite. I don't know how to do this proper way. But I followed this article
I used google access token and passed using postman.
here is my route
Route::get('/customer/login/google', [CustomerAPIController::class,'google']);
here is my function as the article given
public function google(Request $request)
{
$provider = "google";
$token = $request->input('access_token');
$providerUser = Socialite::driver($provider)->userFromToken($token);
// check if access token exists etc..
$user = User::where('provider_name', $provider)->where('provider_id', $providerUser->id)->first();
// if there is no record with these data, create a new user
if($user == null){
$user = User::create([
'provider_name' => $provider,
'provider_id' => $providerUser->id,
]);
}
// create a token for the user, so they can login
$token = $user->createToken(env('APP_NAME'))->accessToken;
// return the token for usage
return response()->json([
'success' => true,
'token' => $token
]);
}
But I got this error when passing the access token,
"Trying to access array offset on value of type null"
I don't know my progress is correct. But I found the issue is coming from this line of the function,
$providerUser = Socialite::driver($provider)->userFromToken($token);
What is the wrong in here? does my whole procedure incorrect.? how to implement this. IF there is a answer or guide, that would be very helpful.(If the description unclear or if there is missing data please inform)

Laravel - Alternative for Session in Laravel Socialite Login in Rest API

I am new to Laravel Socialite Login. In my Laravel-8 Application, I have used:
'Socialite' => Laravel\Socialite\Facades\Socialite::class,
for Azure Socialite Login, which works for the multi-company Login. I store the company id in Session.
public function getCompanies() {
$current_domain = request()->getHost(); // your company app name
$domain = explode('.', $current_domain);
return OrgCompany::where('subdomain', $domain[0])->firstOrFail();
}
public function redirectToProvider(Request $request, OrgSocialiteSetting $azureCredential)
{
$companies = $this->getCompanies();
if($companies){
$company_credentials = OrgSocialiteSetting::where('id', $companies->id)->first();
//when switching between different API Credentials
config(['services.azure.client_id' => $company_credentials->client_id]);
config(['services.azure.client_secret' => $company_credentials->client_secret]);
config(['services.azure.redirect' => $company_credentials->client_redirect]);
session(['id' => $request['company']]);
return Socialite::with('azure')->redirect();
}
return back()->with('status', 'Please select a Valid company');
}
Now I want to convert the same code to Laravel Rest API whereby I consume it in Angular-10
I am using Laravel-Passport and LaravelSocialite as shown above.
Since I am no more using Session, how do I now deal with
session(['id' => $request['company']]);
in the code above?
Thanks

Login by code seems to not work in laravel

Basically i'm trying to send by email a link that lets you login with a specific account and then redirects you to a page.
I can seccessfully generate link and send them via email using URL functionalities in laravel using this code:
Generating the link:
$url = "some/page/".$travel_id;
$link = URL::temporarySignedRoute(
'autologin', now()->addDay(), [
'user_id' => 3,
'url_redirect' => $url,
]
);
And sending the mail:
Mail::send('emails.travel', $data, function ($message) use ($data) {
$message->from('mail#mail.com', 'blablabla');
$message->to('reciever#mail.com', 'blablabla')->subject('test');
});
There is a route that catches the link sent by mail that is supposed to log you in with the user (in this case, the one with the id '3') and redirect you to some page but when it redirects, it prompts you to the login page, as if you are not logged.
Here is the route:
Route::get('/autologin', function (Request $request) {
$user = User::findOrFail($request->user_id);
if (! $request->hasValidSignature()) {
abort(403);
}
Auth::login($user);
return redirect($request->input('url_redirect'));
})->name('autologin');
When i try to do a Auth::check() after the Auth::login($user); it returns true, so is the user logged in?
I also tried to use Auth::loginUsingId($request->user_id); with no different results.
Any idea of what's happening?
So i found the problem,
I was logging in with a backpack user but i was using the default auth feature of laravel.
Turns out i need to use: backpack_auth()->login($user); instead of Auth::login($user); if i want to login using a backpack user.
Also use backpack_auth()->check() instead of Auth::check().

Lumen JWT send token with requests

Authentication is working, I have a few routes under auth middleware, Whenever i request it throws :
{
"message": "Failed to authenticate because of bad credentials or an invalid authorization header.",
"status_code": 401
}
How can i send the token with the request like :
Authorization bearer {{Long token}}
It works with `postman`, How can i send the token with request header, Or in any other best way.
Route :
$api->get('/categories', [
'uses' => 'App\Http\Controllers\CategoryController#index',
'as' => 'api.categories',
]);
Method :
public function index() {
$lessons = \App\Category::all();
$token = JWTAuth::getToken(); // $token have jwt token
return response()->json([
'data' => $lessons,
'code' => 200,
]);
}
The question was pretty vague to answer. Please be more specific from next time. From your comments i could finally realise that you want to consume the api from a mobile app.
You need to return the token generated for an user either during login or during registration or any other authentication method/route you have. The mobile app needs to read this response and store the token locally. Then the app needs to inject this token in the request header for every single request. That's the normal api token workflow.
The app should also be coded to read the error response from requests and if it returns errors for expired or invalid token, the app needs to clear the locally stored token and then request the user to login again to generate a fresh token.
you can use : https://github.com/tymondesigns/jwt-auth
requriment :
Laravel 4 or 5 (see compatibility table)
PHP 5.4 +
Steps:
1 : add below line in composer.json in require array
"tymon/jwt-auth": "0.5.*"
2 : run "composer update" in your terminal
3 : after this you have to register service provider
go to config/app.php
and add 'Tymon\JWTAuth\Providers\JWTAuthServiceProvider' this in provider array
and 'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth' , 'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory' this to aliases array
4 : publish pacakge :
"php artisan vendor:publis --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
5 : generate secrate key in config file
'php artisan jwt:generate'
6 : for addition configuration : https://github.com/tymondesigns/jwt-auth/wiki/Configuration
Usage :
AuthenticateController.php
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthenticateController extends Controller
{
public function authenticate(Request $request)
{
// grab credentials from the request
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
// all good so return the token
return response()->json(compact('token'));
}
}
You can also skip user authentication and just pass in a User object. e.g.
// grab some user
$user = User::first();
$token = JWTAuth::fromUser($user);
The above two methods also have a second parameter where you can pass an array of custom claims. e.g.
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
JWTAuth::attempt($credentials, $customClaims);
// or
JWTAuth::fromUser($user, $customClaims);
create token based on anything
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);
d

Resources