Login path not loading - model-view-controller

I have set my startup class but my login path is giving me issues. It is unable to load the login page. This is my code so far
public void ConfigureServices(IServiceCollection services)
{
IServiceCollection serviceCollections = services.AddDbContext<CISContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ODDB")));
services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<CISContext>();
services.AddControllersWithViews();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie();
services.ConfigureApplicationCookie(option => { option.LoginPath = "/Login"; });
any help on the setup will be appreciated. I am using cookie authentication
Thank you

Related

Net Core Authorize Property Ignore Authentication Cookie Expiration

I'm using OpenId provider to authenticate my NetCore 6 Razor MVC application. Following my program.cs
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(cookie => {
cookie.Cookie.Name = "MyCookie";
cookie.ExpireTimeSpan = TimeSpan.FromMinutes(8);
cookie.Cookie.MaxAge = TimeSpan.FromMinutes(8);
cookie.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
cookie.SlidingExpiration = false;
})
.AddOpenIdConnect(options => {
...
});
and my controller:
[Authorize]
public IActionResult Test()
{
return View();
}
I hope my controller will redirect to login page after 9 minutes or more without activity but it keep ignoring cookies expiration (8 minutes) and show my test page. What went wrong on my code?

How to create HTTP requests with axios from within V8Js

When running Axios in V8Js all requests fail as XMLHttpRequest is not available.
How can we make axios request work server side?
It's possible to bind PHP functions or classes to JavaScript with V8Js.
For example in PHP you can:
$v8 = new \V8Js();
$v8->sayHello = function($name) {
print('Hello ' . $name);
};
$js = 'PHP.sayHello('Bob')';
$v8->executeString($js);
When executed this will produce the string 'Hello Bob'.
So, know this we can create the XMLHttpRequest in PHP and then bind it to JS in the same way.
First we need to create the XMLHttpRequest class to actually make the requests. For this I'm using Guzzle.
<?php
namespace App\Http\Helpers;
use GuzzleHttp\Client;
class XMLHttpRequest
{
protected $url;
protected $method;
public $status;
public $statusText;
public $readyState;
public $responseData;
public $responseHeaders;
public $onreadystatechange;
public function open($method, $url)
{
$this->method = $method;
$this->url = $url;
}
public function send($data = '')
{
$headers = [];
// Here I am using a Laravel function to fetch the session id but this could be replaced
if($sessionId = request()->session()->getId()) {
// Set whatever auth values are needed for your application
$headers['Cookie'] = 'session=' . $sessionId;
}
$options = [
'http_errors' => false,
'headers' => $headers,
'body' => $data
];
$client = new Client();
$response = $client->request($this->method, $this->url, $options);
$this->responseHeaders = $response->getHeaders();
$this->responseData = (string) $response->getBody();
$this->status = $response->getStatusCode();
$this->readyState = 4;
$this->statusText = $response->getReasonPhrase();
if (is_callable($this->onreadystatechange)) {
call_user_func($this->onreadystatechange);
}
}
}
Then after creating a V8Js instance we can attach the new library:
$v8 = new \V8Js();
$v8->XMLHttpRequest = function () {
return new XMLHttpRequest;
};
Now we need to tell Axios to use our library. We can do this by first creating an adapter:
const XMLHttpRequest = PHP.XMLHttpRequest;
const adapter = (config) => {
return new Promise(function(resolve, reject) {
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = () => {
const response = {
data: xhttp.responseData,
status: xhttp.status,
statusText: xhttp.statusText,
headers: xhttp.responseHeaders,
config: config,
request: {}
};
settle(resolve, reject, response);
};
xhttp.open(config.method, config.baseURL + config.url);
xhttp.send();
})
}
And then adding an interceptor to set the adapter and the baseURL:
axios.interceptors.request.use(config => {
config.baseURL = 'https://YOUR-URL.com'
config.adapter = adapter
return config
})
After this using a normal axios.post will work server side.

Force slack logging into queue

I have a bit of a dilemma as I need to come up with a good logger that logs what is happening in the app at the same time if there is a Log::error called, it should also notify Devs and Sys admin via slack. It is currently working, but it adds an overhead to the request-response time.
Below is my setting:
//config/logging.php
'default' => env('LOG_CHANNEL', 'stack'),
//truncated
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily', 'slack'],
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 0,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'App',
'emoji' => ':boom:',
'level' => 'error',
]
]
//truncated
//UserController
public function show(User $user)
{
//just a sample code, the important part is where the Log facade is called
try {
//business logic
} catch (Exception $e) {
Log::error(get_class(), [
'user_id' => $user->id,
'message' => $e->getMessage()
]);
}
return view('user.show', compact($user));
}
It is already working, but for sure we can still improve this to reduce the overhead somehow even though the added time for code above is negligible, but the real code is more complex and has quite a lot of iteration
How can I alter modify the behavior of the 'slack' logger to push it into a queue when it is triggered? I prefer to code it once and forget it rather than remembering that I have to push it to an on-demand logger such as
Log::chanel(['daily', 'slack'])->...
OR
//this is good for more on particular event notification but not not error notification which can happen anywhere
Notification::route('slack', env('LOG_SLACK_WEBHOOK_URL'))->notify(new AlertDevInSlackNotification)`
Note:
I tried adding some code into bootstrap/app.php but it is not working
//bootstrap/app.php
$app->configureMonologUsing(function($monolog) use ($app) {
//some code here. does not work, just getting page not working
});
It is like when I call this log level and this channel, I want it to be queued
You can do like this.
1.Create Job ex: name as LogSlackQueue.php
public class LogSlackQueue implements ShouldQueue {
...
...
public function handle() {
Log::channel(['daily', 'slack'])->info($your_input);
}
}
2.Then use as
LogSlackQueue::dispatch($your_input)
If you dont want to do like above, you need to figure it out to make custom provider
Thanks to #ZeroOne for giving out idea on how to solve it. I wanted it automatic and any existing code having Log::error() will automatically prompt the devs.
Below is my solution.
//CustomSlackServiceProvider.php
try {
//listen to all events
Event::listen('*', function($event, $details) {
//check if the event message logged event which is triggered when we call Log::<level>
if($event == "Illuminate\Log\Events\MessageLogged") {
//$details contain all the information we need and it comes in array of object
foreach($details as $detail) {
//check if the log level is from error to emergency
if(in_array($detail->level, ['emergency', 'critical', 'alert', 'error'])) {
//trigger the notification
Notification::route('slack', env('LOG_SLACK_WEBHOOK_URL'))->notify(new AlertDevInSlackNotification($detail->message, $detail->level, $detail->context));
}
}
}
});
} catch (Exception $e) {
}
//AlertDevInSlackNotification.php
class AlertDevInSlackNotification extends Notification implements ShouldQueue
{
use Queueable;
private $class;
private $level;
private $context;
public function __construct($class, $level, $context)
{
$this->class = $class;
$this->level = strtoupper($level);
$this->context = $context;
//prevent congestion in primary queue - make sure this queue exists
$this->queue = 'alert';
}
public function via($notifiable)
{
return ['slack'];
}
public function toSlack($notifiable)
{
return (new SlackMessage)
->content($this->level.': '.$this->class)
->attachment(function($attachment) {
$attachment->fields($this->context);
});
}
UPDATE:
The code above will work when you trigger Log::error().
But to listen to an event that is being thrown by an error such as syntax error which will cause "Serialization of 'Closure' is not allowed". You can do this instead to improve coverage:
public function boot()
{
try {
//listen to all events
Event::listen('*', function($event, $details) {
//check if the event message logged event which is triggered when we call Log::<level>
if($event == "Illuminate\Log\Events\MessageLogged") {
// dump($event);
//$details contain all the information we need and it comes in array of object
foreach($details as $detail) {
$this->path = '';
$this->level = '';
$this->context = [];
$this->message = '';
//check if the log level is from error to emergency
if(in_array($detail->level, ['emergency', 'critical', 'alert', 'error'])) {
//#todo - exclude: Error while reading line from the server. [tcp://cache:6379] = restart
//check if the context has exception and is an instance of exception
//This is to prevent: "Serialization of 'Closure' is not allowed" which prevents jobs from being pushed to the queue
if(isset($detail->context['exception'])) {
if($detail->context['exception'] instanceof Exception) {
$this->level = $detail->level;
//to keep consistency on all the log message, putting the filename as the header
$this->message = $detail->context['exception']->getFile();
$this->context['user'] = auth()->check() ? auth()->user()->id.' - '. auth()->user()->first_name.' '.auth()->user()->last_name : null;
$this->context['message'] = $detail->context['exception']->getMessage();
$this->context['line'] = $detail->context['exception']->getLine();
$this->context['path'] = request()->path();
$this->runNotification();
continue;
}
}
$this->level = $detail->level;
$this->context = $detail->context;
$this->message = $detail->message;
$this->runNotification();
continue;
}
}
}
});
} catch (Exception $e) {
}
}
public function runNotification()
{
Notification::route('slack', env('LOG_SLACK_WEBHOOK_URL'))->notify(new AlertDevInSlackNotification($this->message, $this->level, $this->context));
}

Social Login with Angular 5 and Lumen 5.6

I am trying to implement social login with angular 5 as my front-end and lumen 5.6 as my backend.
I am using JWT Authentication system with Lumen.
So, I am confused here how I should implement social login system in this situation.
Through my research I have come to these libraries which can do that work, but I am not sure how the process is handled from the front-end (angular) to back-end (lumen).
For Angular 5 -
angularx-social-login OR Satellizer
For Lumen -
laravel/socialite
But as I have not found any documentation on Satellizer working with Angular 5, so I choosed to use other one.
I have read this article, but still the process from front-end to back-end is not clear to me.
https://medium.com/#barryvdh/oauth-in-javascript-apps-with-angular-and-lumen-using-satellizer-and-laravel-socialite-bb05661c0d5c
Any explanatory help ?
So the way I used social login with Angular 5 is with a package called
"angular5-social-login": "^1.0.9",
So add that to your package.json file.
Import it in app.module.ts
import { SocialLoginModule, AuthServiceConfig, GoogleLoginProvider, FacebookLoginProvider } from 'angular5-social-login';
Set up a function in app.module.ts
export function getAuthServiceConfigs() {
const config = new AuthServiceConfig(
[
{
id: FacebookLoginProvider.PROVIDER_ID,
provider: new FacebookLoginProvider('') // Left as i dont use it
},
{
id: GoogleLoginProvider.PROVIDER_ID,
provider: new GoogleLoginProvider('YOUR-API-TOKEN.apps.googleusercontent.com')
},
]
);
return config;
}
Add it to your Imports in app.module.ts
imports: [
HttpClientModule,
AppRoutingModule,
...
SocialLoginModule, // One we need to add
],
Then at the add it to your providers in app.module.ts
providers: [
YourServices,
...
ApiAuthService,
{
provide: AuthServiceConfig,
useFactory: getAuthServiceConfigs
},
LoggedInGuard,
],
As you can see i have a LoggedInGuard and a ApiAuthService these are these with the auth and checking your logged in.
So That's the package installed and set up...
Now inside of api-auth.service.ts add this function
socialSignIn(userData) {
const formData = new FormData();
formData.append('email', userData.email);
formData.append('name', userData.name);
formData.append('provider', userData.provider);
formData.append('id', userData.id);
formData.append('idToken', userData.idToken);
formData.append('token', userData.token);
formData.append('image', userData.image);
return this._http.post(
environment.apiUrl + '/auth/social-signin/',
formData,
{
headers: new Headers({
'Authorization': 'Bearer ' + userData.idToken
})
}
);
}
Now in your sign in component add this to the HTML
<div (click)="socialSignIn('google')" class="c2a_btn large google">
Log in with google
</div>
In your sign in component .ts file add this function
import { AuthService, FacebookLoginProvider, GoogleLoginProvider, LinkedinLoginProvider } from 'angular5-social-login';
import { ApiAuthService } from '../../../../services/api-auth.service';
import { TokenService } from '../../../../services/token.service';
public socialSignIn(socialPlatform: string) {
this.loading = true;
let socialPlatformProvider;
if (socialPlatform === 'facebook') {
socialPlatformProvider = FacebookLoginProvider.PROVIDER_ID;
} else if (socialPlatform === 'google') {
socialPlatformProvider = GoogleLoginProvider.PROVIDER_ID;
} else if (socialPlatform === 'linkedin') {
socialPlatformProvider = LinkedinLoginProvider.PROVIDER_ID;
}
this.socialAuthService.signIn(socialPlatformProvider).then(
(userData) => {
this._apiAuthService.socialSignIn(userData)
.map( data => {
return data.json();
})
.subscribe(
token => {
this._tokenService.setAccessToken(token.access_token);
},
error => {
this.invalidLogin = true;
this.loading = false;
},
() => {
this.loading = false;
this.closeSignIn.emit('out');
// this._router.navigate(['/profile']);
}
);
}
);
}
This is just the front end now for the back end. I'm using Laravel 5.6
But I made a function like this
public function socialSignIn(Request $request, Response $response) {
$date = date('Y-m-d h:i:s');
$provider = $request->input('provider');
if ($provider == 'google') {
$id_token = $request->header('Authorization');
$id_token = str_replace("Bearer ","",$id_token);
$CLIENT_ID = Config::get('google.client_id');
$email = $request->input('email');
$names = $request->input('name');
$name = explode(' ', $names);
$client = new \Google_Client();
$client->setDeveloperKey($CLIENT_ID);
$payload = $client->verifyIdToken($id_token);
if ($payload) {
if (User::where('email', '=', $email)->exists()) {
$user = User::Where('email', '=', $email)->first();
if(!Auth::loginUsingId($user->id)){
return response()->json([
'failed'
], 403);
}
$updateLastLoginDate = User::where('id', Auth::user()->id)-first();
$updateLastLoginDate->last_login_date = $date;
$updateLastLoginDate->save();
$activeAccount = Auth::user();
$activeAccount->active = '1';
$activeAccount->save();
} else {
$recordUser = New User;
$recordUser->email = $request->input('email');
$recordUser->last_login_date = $date;
$recordUser->save();
$recordLinkedSocialAcounts = new LSA;
$recordLinkedSocialAcounts->user_id = $recordUser->id;
$recordLinkedSocialAcounts->provider_name = $provider;
$recordLinkedSocialAcounts->provider_id = $request->input('id');
$recordLinkedSocialAcounts->save();
$recordUserInformation = new UPI;
$recordUserInformation->user_id = $recordUser->id;
$recordUserInformation->first_name = $name[0];
$recordUserInformation->last_name = $name[1];
$recordUserInformation->last_login_date = $date;
$recordUserInformation->image = $request->input('image');
$recordUserInformation->save();
if(!Auth::loginUsingId($recordUser->id)){
return response()->json([
'failed'
], 403);
}
}
return response()->json([
'access_token' => Auth::user()->createToken('access_token')->accessToken,
'role_id' => Auth::user()->role_id
], 200);
} else {
return response()->json([
'failed'
], 403);
}
}
}
I will most probably make a video on this soon. Any questions just ask

How to process creditcard payment option on your website (without redirect to paypal) paypalexpressceckout with ci-merchant library

I am using ci-merchant library and integrated it succesfully and also works for paypal account owner user.But dont know how to processs for user who dont have paypal acc and wants to pay via credit or debit card on my website only*(without redirect to paypal)* any idea????abt that.....this is the code i use for the normal paypal payment in my controller and works good as well..
$this->load->library('merchant');
$this->merchant->load('paypal_express');
$settings = $this->merchant->default_settings();
$settings = array(
'username' => 'takeout_api1.rest.com',
'password' => '1369227981',
'signature' => 'AnOQDpMvzNQqHN5u7vb9BKLaKYLoALq6R0g3ohOwD4RQgO0DQDI5l7V4',
'test_mode' => true,
);
$this->merchant->initialize($settings);
$params = array(
'amount' => 1500.00,
'currency' => 'CAD',
'return_url' => 'http://192.168.1.7/takeout/order_detail/test',
'cancel_url' => 'http://192.168.1.7/takeout/order_detail/test');
$response = $this->merchant->purchase($params);
function test()
{
$settings = array(
'username' => 'takeout_api1.rest.com',
'password' => '1369227981',
'signature' => 'AnOQDpMvzNQqHN5u7vb9BKLaKYLoALq6R0g3ohOwD4RQgO0DQDI5l7V4',
'test_mode' => true);
$this->merchant->initialize($settings);
$params = array(
'amount' => 1500.00,
'currency' => 'CAD',
'return_url' => 'http://192.168.1.7/takeout/order_detail/test',
'cancel_url' => 'http://192.168.1.7/takeout/order_detail/test');
$response = $this->merchant->purchase_return($params);
if ($response->success())
{
// mark order as complete
echo "yo";
exit;
}
else
{
$message = $response->message();
echo('Error processing payment: ' . $message);
exit;
}
}
You could interface your Merchant services
interface merchantServiceInterface
{
public function initialize();
public function purchase();
public function purchase_return();
}
Paypal
class Paypal implements merchantServiceInterface
{
public function initialize(){}
public function purchase(){}
public function purchase_return(){}
}
Credit/Debit Cards
class Realex implements merchantServiceInterface
{
public function initialize(){}
public function purchase(){}
public function purchase_return(){}
}
Now in your form, have a little radio button group and ask the user to select
either paypal or credit/debit card
<label>Choose a payment Method</label>
<label>Paypal<label>
<input type="radio" name="merchant" value="paypal" />
<label>Credit/Debit Card<label>
<input type="radio" name="merchant" value="debit" />
Merchant Library
class Merchant
{
protected $_service;
public function __construct(merchantServiceInterface $service)
{
$this->_service = $service;
}
public function initialize()
{
// Will either run Paypal/Realex initialize()
// Depending on what instance was passed to _service
//
$this->_service->initialize();
}
}
Controller
class Controller extends CI_Controller
{
public function method()
{
if($this->input->post('merchant') == 'paypal')
{
$service = new Paypal();
}
elseif($this->input->post('merchant') == 'debit')
{
$service = new Realex();
}
$this->load->library('Merchant', $service);
$this->merchant->initialize();
}
}
Edit to answer your comment
I just used Realex as an example
You need to figure out what both libraries have in common, or at a very low level abstraction figure out what they share.
an example would be
They both need a initialize method to configure options
They both need to send a request to an API
They both need a responce
etc etc keep abstracting away
How you handle these, will be unique to the library itself.
interface merchantServiceInterface
{
// Use the facade design pattern here
// so configuration is done in each library
public function initialize();
// Send a request with data
// Paypal - use http_build_query and curl
// Realex - use xml and curl
public function request(array $data);
public function responce();
}
Paypal Express Checkout doesn't support taking credit cards on your site. It is an off-site gateway, so redirect is mandatory.
You need to explore using PayPal Pro, Payflow, or any number of other gateways which support accepting credit cards directly on your website (plus the extra PCI requirements which come with that).

Resources