Laravel Socialite Github - error 401 Unauthorized/Bad credentials - laravel

I've got fresh installation of Laravel 9 with Socialite package. This is my config/services.php file:
'github' => [
'client_id' => 'xxx',
'client_secret' => 'xxx',
'redirect' => 'http://localhost:3000/login',
],
From my frontend Nuxt application, I redirect to github oauth page https://github.com/login/oauth/authorize?client_id=xxx which works fine. I confirm authorization for my account and then it redirects me back to my frontend http://localhost:3000/login as this URL is set in my GitHub settings for Authorization callback URL.
In return URL I have my code for authorization: http://localhost:3000?code=xxx
Then I do axios call to my Laravel server:
axios({
method: "post",
url: 'http://localhost/api/auth/socialite/github',
data: qs.stringify({
client_id: 'xxx',
client_secret: 'xxx',
code: this.$route.query.code,
redirect_uri: 'http://localhost:3000/login',
response_type: 'code'
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded"
}
})
On server side, in routes/api.php I have this route:
Route::post("auth/socialite/{provider}", function($provider){
return Socialite::driver($provider)->stateless()->user();
});
And when I'm trying to return user from github using Socialite, it returns this error:
Client error: `GET https://api.github.com/user` resulted in a `401 Unauthorized` response:{"message":"Bad credentials","documentation_url":"https://docs.github.com/rest"}
So Socialite calls this URL https://api.github.com/user with my credentials and is trying to get user from github if I understand it right. But somehow it returns the error. I have checked my client_id, client_secret, redirect URL...
I'm sure everything works fine until I call Socialite::driver..., because If I kill the process with for example dd($provider) before Socialite::driver... it returns "github" as it should. And If I kill the process and take generated code parameter from github page and I'm trying to do the same call on Postman with my credentials, Github returns me token.
So I would expect for Socialite call to do the same.
I have two questions:
Is my structure of code (communication of frontend with backend and back, usage of Socialite package...) right?
Why it is showing me error when the same call in Postman works?

Related

MSAL and OAuth 2.0 - Request an authorization code programmatically

Goal is to get access token from MSAL programmatically for Cypress e2e tests.
We use V2.0 API.
According to this I first need to get the authorization code: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code
to get the access token https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-access-token
So in order to get authorization code I would need to do this request
// GET
// Line breaks for legibility only
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=openid%20offline_access%20https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&state=12345
&code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl
&code_challenge_method=S256
But this returns text/html so I would need to manually login to get the code.
Is there any way to progammatically to get the authorization code?
This is how I got it solved by creating a login command. The command fetches the token programatically and stores it into localStorage.
import 'cypress-localstorage-commands';
Cypress.Commands.add('login', () => {
const request = {
method: 'POST',
form: true,
url: `https://login.microsoftonline.com/${Cypress.config('tenantId')}/oauth2/v2.0/token`,
body: {
grant_type: 'client_credentials',
client_id: Cypress.config('clientId'),
client_secret: Cypress.config('clientSecret'),
scope: `${Cypress.config('clientId')}/.default`,
},
};
cy.request(request).then(response => cy.setLocalStorage('msal.idtoken', response.body.access_token));
});

"The GET method is not supported for this route. Supported methods: POST" message in my laravel API

I followed a tutorial to do a login page with an external api, but my API is not working.
In the localhost http://localhost:8000/api/auth/login appear "The GET method is not supported for this route. Supported methods: POST." and in the Chrome DevTools
headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure response for http://127.0.0.1:8000/api/auth/register: 422 Unprocessable Entity"
name: "HttpErrorResponse"
ok: false
status: 422
statusText: "Unprocessable Entity"
url: "http://127.0.0.1:8000/api/auth/register"
I use laravel 6,php 7, ionic 4
Please, help me!
(tutorial is: https://blog.flicher.net/laravel-rest-api-passport-authentication-for-ionic-app/)
If you follow the tutorial in section Step 6 — Set API routes it list down all routes for api.
Here it says as below
File: api.php
Route::group([
'prefix' => 'auth'
], function () {
Route::post('login', 'Auth\AuthController#login')->name('login');
});
That means we have a route with name as login which would be a POST request and url would be api/auth/login as it is api.php file.
You are calling it from the browser, that would be a get request, which is not found by laravel in routes, so it is generating this error.
Reference: Laravel Routing

Can't get auth user with laravel passport, keep getting "Unauthenticated" error

I can't get the information of the authenticated user in a Laravel passport app with JWT and vue.
I've installed laravel passport. Ive done everything in the documentation and added:
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
To consume it with js for a SPA app.
I've protected my routes with the auth:api middleware, but i keep getting:
{"Status":{"api_status":0,"Code":401,"Message":"Unauthenticated"}}
When i use postman to manually insert the CSRF-TOKEN in Authorization Bearer Token. It does give me the auth user.
Whatever i do, i keep getting null on Auth::user(); in my controllers and routes
Laravel V5.7
Node V10.15.3
Npm V.6.9.0
You need to send a POST request (using Postman/Insomnia) with the details of the user you want to log in as to /oauth/token in your app which will respond with an API token. You save this api token locally, and then add add it as a Header variable to your guzzle/axios/whatever function's http calls (every one of them!) to your API.
$http = new GuzzleHttp\Client;
$data = 'Whatever';
$response = $http->request('POST','api/user',[
'data' => $data,
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer xxxxxxxxxxxxThis is the long authentication string returned from the Postman requestxxxxxxxxxxxxxx'
]
]);
dd(json_decode((string) $response->getBody())); // To view the response
From: https://laravel.com/docs/5.5/passport#creating-a-password-grant-client

How to authenticate Vue.js / Axios request of an API route in Laravel

I'm in Laravel 5.6. I have all my API routes built out and properly responding to requests from my REST client (Paw). I'm trying to build a simple front end to access those routes.
I'm trying to use Laravel's out-of-the-box features as much as possible, so I'm using Axios to call those routes from a blade template using Vue.js. It works if I disable auth middleware on the test route, but I get 401 errors on the console when auth middleware is enabled for the route.
The problem seems obvious enough... The auth:api guard on my /api routes wants to see an oauth token in the header, but when I log in with the web page it does session authentication. I assume there's a simple way to resolve this without having to spoof an oauth token request in the web frontend, right? Do I need to somehow pass the session token in my request with Axios? And, if so, do I also need to change the auth:api guard in my api routes file?
I solved it! I'm a bit embarrassed because the answer was actually in the Laravel docs, but I will say I tried this before posting the question here and it wasn't working. Perhaps something else was broken at the time.
Per the Laravel docs:
All you need to do is add the CreateFreshApiToken middleware to your
web middleware group in your app/Http/Kernel.php file:
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
This Passport middleware will attach a laravel_token cookie to your
outgoing responses. This cookie contains an encrypted JWT that
Passport will use to authenticate API requests from your JavaScript
application. Now, you may make requests to your application's API
without explicitly passing an access token...
You will probably want to use Larvel Passport or a JWT auth mechanism for obtain the Authorization token.
Seeing as how you're using axios, add a request interceptor to attach the access token to every request once you successfully authenticate. A simple example:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// assume your access token is stored in local storage
// (it should really be somewhere more secure but I digress for simplicity)
let token = localStorage.getItem('access_token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
to use the auth:api first you need api_token inside your users table
Schema::table('users', function ($table) {
$table->string('api_token', 80)->after('password')
->unique()
->nullable()
->default(null);
});
also you can create a user for testing as follows
User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'api_token' => Str::random(60),
]);
in your layout use the following before #yield('content')
<script>
window.Laravel = <?php echo json_encode(['api_token' => (Auth::user())->api_token]); ?>
</script>
now you can use window.laravel.api_token inside your vue js to use it in headers
heres an example
var methods = new Vue({
el: '#tabs_lists',
data: {
config: {
headers: {
Authorization: 'Bearer ' + window.Laravel.api_token,
Accept: 'application/json'
}
},
data: []
},
methods: {
test: function (link) {
axios.get(link, this.config)
.then(response => (this.data = response.data)).catch(function (error) {
// handle error
console.log(error);
});
}
}
}
)

Okta Authentication works but Get User by Id gives Invalid Token Provided

I have a Django app that authenticates using Okta:
headers = {
'Authorization': 'SSWS {}'.format(<okta api token>),
'Accept': 'application/json',
'Content-Type': 'application/json'
}
authentication_payload = {
'username': <username>,
'password': <password>
}
response = requests.post(
<okta auth endpoint>,
headers=headers,
data=json.dumps(authentication_payload)
)
This works successfully. From the response content I am able to get the User Id:
content = json.loads(r.content.decode('utf-8'))
okta_user_id = content['_embedded']['user']['id']
I then use the okta_user_id to create the endpoint to get the okta user by id:
okta_user_endpoint = https://<org>.okta.com/api/v1/users/<okta_user_id>
I then use the same headers from the authentication call, with the same api token, and try to get the user by id:
user_response = requests.get(
okta_user_endpoint,
headers=headers
)
But this is unsuccessful. I get a 401 error with the following content:
{
"errorCode":"E0000011",
"errorSummary":"Invalid token provided",
"errorLink":"E0000011",
"errorCauses":[]
}
Seems straight forward with an invalid token, but if the token is invalid how am I able to successfully make the authentication call? And if the token if valid for the authentication call why is it not working to get the user by id?
Okta recently changed the way that the /authn endpoint works. The /authn endpoint no longer requires an authentication token. This was done in order to support single-page applications.
It looks like your application will need to be able to fetch user information on an arbitrary user. In that case, using an Okta API token makes sense.
However, if you were making that call from a single-page application, you would want to make a request to the /users/me API endpoint.

Resources