Flutter : method call on null when using shared preference - api

I want to use shared preference to keep user login and i'm using api with provider .
i tried to put it in main but i don't how to do it because i have two different page to go so i was used it in the first page but i found another error
[ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception:
NoSuchMethodError: The method 'login' was called on null. E/flutter (
4199): Receiver: null
and this is the method which i used
UserProvider userProvider;
ServiceProvider serviceProvider;
bool alive = true;
bool isLoading = false;
var page;
#override
initState() {
init();
super.initState();
}
init() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool language = prefs.getBool('language');
bool type = prefs.getBool('type');
bool login = prefs.getBool('islog');
if (alive) {
if (language == true && type == true && login == true) {
signIn();
} else if ((language == true && type == false && login == true)) {
visitor();
}
} else {
if (alive) {
setState(() {
isLoading = false;
});
}
}
}
signIn() async {
//var res = await userProvider.residentLogin(context, phone, pass);
SharedPreferences prefs = await SharedPreferences.getInstance();
var phone = prefs.getString('phone');
String pass = prefs.getString("pass");
bool type = prefs.getBool('type');
print("Shared ***************resident********************************");
await userProvider.residentLogin(context, phone, pass);
await serviceProvider.getHomeServiceList(context);
await serviceProvider.getAdsList(context);
await serviceProvider.getVideosList(context);
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) => ResidentBottomTab()));
print('Hello Tourist !!');
}
visitor() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var phone = prefs.getString('phone');
String pass = prefs.getString("pass");
await userProvider.login(context, phone, pass);
await serviceProvider.getVisitorAdsList(context);
await serviceProvider.getVisitorVideosList(context);
bool type = prefs.getBool('type');
print("Shared ******************visitor*****************************");
if (alive) {
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => VisitorHome()));
print('Hello Tourist !!');
}
}
#override
void dispose() {
alive = false;
super.dispose();
}
login method
Future<VisitorLoginModel> login(BuildContext context, phone, pass) async {
final Map<String, dynamic> body = {
'phone': phone,
'password': pass,
'token': '123',
'serial_number': '123',
'lang': 'en',
'os': 'android',
};
_isLoading = true;
notifyListeners();
print('Starting request');
http.Response response = await http.post(Environment.userLogin,
body: json.encode(body), headers: Environment.requestHeader);
print('Completed request');
print('user SignUp response : ${response.body}');
//print(body.toString());
Map<String, dynamic> res = json.decode(response.body);
var results;
if (res['status'] == 1) {
// login successful
_visitorLoginModel = parseVisitorLogin(response.body);
Provider.of<LoginVisitorUserProvider>(context, listen: false).userData =
visitorLoginModel;
Provider.of<LoginVisitorUserProvider>(context, listen: false)
.sessionToken = visitorLoginModel.data.token;
headers = Provider.of<LoginVisitorUserProvider>(context, listen: false)
.httpHeader;
// print('token');
print('this is session token' +
Provider.of<LoginVisitorUserProvider>(context, listen: false)
.sessionToken);
} else {
// login failed;
results =
FailedRequest(code: 401, message: res['massage'], status: false);
}
_isLoading = false;
notifyListeners();
return visitorLoginModel;
}
so anyone has an idea to solve this case !

The method call await userProvider.login(context, phone, pass); is being done on a non initialized variable. UserProvider userProvider; has been declared but not initialized

The issue here is the userProvider is being declared but not initialized and the same goes for serviceProvider
In Flutter any uninitialized variable will implicitly assigned with null
So, at the line await userProvider.login(context, phone, pass); where you call login method in which userProvider is not only initialized
To Solve the issue do the following,
In initState,
userProvider = Provider.of<UserProvider>(context, listen: false);

Related

oauth2 in core 3.1

I am using core 3.1 to connect to the canvas API, this is part of my code..
services.AddAuthentication(config =>
{
config.DefaultAuthenticateScheme = "CanvasCookies";
config.DefaultSignInScheme = "CanvasCookies";
config.DefaultChallengeScheme = "CanvasLMS";
})
.AddCookie("CanvasCookies")
.AddOAuth("CanvasLMS", config =>
{
var canvas_domain = Configuration.GetValue<string>("Canvas:Domain");
var client_secret = Configuration.GetValue<string>("Canvas:Secret");
var client_id = Configuration.GetValue<string>("Canvas:Client_id");
config.ClientId = client_id;
config.ClientSecret = client_secret;
config.CallbackPath = new PathString("/oauth/callback");
//config.Scope.Add("google.com")
config.AuthorizationEndpoint = $"{canvas_domain}login/oauth2/auth";
config.TokenEndpoint = $"{canvas_domain}login/oauth2/token";
config.UserInformationEndpoint = $"{canvas_domain}api/v1/users//courses";
config.SaveTokens = true;
config.Events = new OAuthEvents()
{
OnCreatingTicket = context =>
{
var accessToken = context.AccessToken;
var base64payload = accessToken.Split('.')[1];
var bytes = Convert.FromBase64String(base64payload);
var jsonPayload = Encoding.UTF8.GetString(bytes);
var claims = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonPayload);
foreach(var claim in claims)
{
context.Identity.AddClaim(new Claim(claim.Key, claim.Value));
}
return Task.CompletedTask;
}
this is the controller
public class APICanvasController : Controller
{
...
[Authorize]
public async Task<IActionResult> Secret()
{
var serverResponse = await AccessTokenRefreshWrapper(
() => SecuredGetRequest("https://localhost:44388/secret/index"));
var apiResponse = await AccessTokenRefreshWrapper(
() => SecuredGetRequest("https://localhost:44388/secret/index"));
return View();
}
private async Task<HttpResponseMessage> SecuredGetRequest(string url)
{
var token = await HttpContext.GetTokenAsync("access_token");
var client = _httpClientFactory.CreateClient();
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
return await client.GetAsync(url);
}
public async Task<HttpResponseMessage> AccessTokenRefreshWrapper(
Func<Task<HttpResponseMessage>> initialRequest)
{
var response = await initialRequest();
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
await RefreshAccessToken();
response = await initialRequest();
}
return response;
}
private async Task RefreshAccessToken()
{
...
}
}
}
when I execute the code I obtain this error
Exception: The oauth state was missing or invalid.
Unknown location
Exception: An error was encountered while handling the remote login.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler.HandleRequestAsync()
Any idea what I am doing wrong?
Thanks
CallbackPath is not supposed to refer to a controller, it refers to a unique path handled by the auth middleware. It will redirect back to your controller when it's done.
"/oauth/callback" should handle oauth authentication result as a json instead of page.

How to redirect already authorized user back to the Previous page in identity server 4 .NET CORE?

Below is the code added in Auth Server and Client machine
If any more info needed on this please comment.
StartUp.cs -- Auth Server
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
var builder = services.AddIdentityServer(options =>
{
options.EmitStaticAudienceClaim = true;
})
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients)
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
/*.AddProfileService<ProfileService>()*/;
builder.AddDeveloperSigningCredential();
services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
//services.AddTransient<IProfileService, ProfileService>();
}
StartUp.cs -- Client App
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = "https://localhost:44356/";
options.ClientId = "TestIdpApp";
options.ResponseType = "code";
//options.UsePkce = false;
//options.CallbackPath = new PathString("...")
options.Scope.Add("openid");
options.Scope.Add("profile");
options.SaveTokens = true;
options.ClientSecret = "secret";
});
}
ResourceOwnerPasswordValidator.cs -- Auth Server
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
try
{
UserInfo user = await GetUserDetails(context.UserName, context.Password).ConfigureAwait(false);
if (user != null && string.IsNullOrEmpty(user.ErrorMessage))
{
//set the result
context.Result = new GrantValidationResult(
subject: user.UserId.ToString(),
authenticationMethod: "custom",
claims: GetUserClaims(user));
return;
}
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, user.ErrorMessage);
return;
}
catch (Exception ex)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid username or password");
}
}
//build claims array from user data
public static Claim[] GetUserClaims(UserInfo user)
{
return new Claim[]
{
new Claim("user_id", user.UserId.ToString() ?? ""),
new Claim(JwtClaimTypes.Name, (!string.IsNullOrEmpty(user.FirstName) && !string.IsNullOrEmpty(user.Surname)) ? (user.FirstName + " " + user.Surname) : ""),
new Claim(JwtClaimTypes.GivenName, user.FirstName ?? ""),
new Claim(JwtClaimTypes.FamilyName, user.Surname ?? ""),
new Claim(JwtClaimTypes.Email, user.Email ?? "")
};
}
}
Login Logic ---
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
// check if we are in the context of an authorization request
var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
if (button == "reset")
{
return Redirect("ResetPasswordWithoutCode");
}
else
{
// the user clicked the "cancel" button
if (button == "cancel")
{
if (context != null)
{
// if the user cancels, send a result back into IdentityServer as if they
// denied the consent (even if this client does not require consent).
// this will send back an access denied OIDC error response to the client.
await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied);
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", model.ReturnUrl);
}
return Redirect(model.ReturnUrl);
}
else
{
// since we don't have a valid context, then we just go back to the home page
return Redirect("~/");
}
}
if (ModelState.IsValid)
{
ResourceOwnerPasswordValidationContext context1 = new ResourceOwnerPasswordValidationContext();
context1.UserName = model.Username;
context1.Password = model.Password;
// validate username/password against in-memory store
await _resourceOwner.ValidateAsync(context1);
if (context1.Result.Subject!=null && context1.Result.Subject.Identity.IsAuthenticated)
{
var user = await ResourceOwnerPasswordValidator.GetUserDetails(model.Username,model.Password).ConfigureAwait(false);
await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.UserId, user.Username, clientId: context?.Client.ClientId));
// only set explicit expiration here if user chooses "remember me".
// otherwise we rely upon expiration configured in cookie middleware.
AuthenticationProperties props = null;
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
// issue authentication cookie with subject ID and username
var isuser = new IdentityServerUser(user.UserId)
{
DisplayName = user.Username
};
await HttpContext.SignInAsync(isuser, props);
if (context != null)
{
if (context.IsNativeClient())
{
// The client is native, so this change in how to
// return the response is for better UX for the end user.
return this.LoadingPage("Redirect", model.ReturnUrl);
}
// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
return Redirect(model.ReturnUrl);
}
// request for a local page
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
else if (string.IsNullOrEmpty(model.ReturnUrl))
{
return Redirect("~/");
}
else
{
// user might have clicked on a malicious link - should be logged
throw new Exception("invalid return URL");
}
}
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId));
ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
return View(vm);
}
}
I'm calling a webapi to check the credentials and return info about user like First name last name.
RedirectUri - https://localhost:44356/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3DTestIdpApp%26redirect_uri%3Dhttps%253A%252F%252Flocalhost%253A44335%252Fsignin-oidc%26response_type%3Dcode%26scope%3Dopenid%2520profile%26code_challenge%3DLEZaLWC8ShzJz6LGUsdeUPr974clsUSYVPXWDNmbwOE%26code_challenge_method%3DS256%26response_mode%3Dform_post%26nonce%3D637360236231259886.MmIxNjlhODMtZTJhYy00YzUzLTliYjMtZWJmNzM3ZjRiM2VlZmYwYzI2MDAtNWRjYS00NThlLWI4MjAtY2ViYjgxY2RlYmZi%26state%3DCfDJ8LFJDL-5o71Ao2KksnBDgPVrH1DIIiM9LZSGUG43HRwLS6OjGUiGPwZ_xxT1RVryTZh7z3zwezVbdiy1L94mFlWausuYQrDNTWtzxrpTf2CrKjHRjcUIyNt5tX_g-yZYkWvxzCiyrpxnp7cctbNGoCmj_kqidhxCWsZee_26c3eVqfJfH7XEDfKUMj2BHeKQe_Ar9f2SkZJ0SBuy6MBe6zpU7-DDOYotDn-oO5zrtaHL8GCZfSqckqalL5yaGeolZ1ZDcubY01InyrBh1NwlVQRdGZRRWIZ-WnqqFKrTboQyw4rQswR-7BaLTtL8QitRkUmwS17LBLUvXKRBs8C0NUsX9HyREnmCVG2qW6s2AVpnE4iSt4XVSRcY-crXml2FjA%26x-client-SKU%3DID_NETSTANDARD2_0%26x-client-ver%3D5.5.0.0
You could have something like this in your GET Login action:
if (this.User.Identity.IsAuthenticated)
{
return Redirect(returnUrl);
}

How to setup a base URL and where do I declare it in flutter dio for api calls?

like how to fix boilerplate code in separate file and use it in ui pages.
I Need to declare this uri variable in separate file and access across over all pages:
static var uri = "https://xxx/xxx/web_api/public";
static BaseOptions options = BaseOptions(
baseUrl: uri,
responseType: ResponseType.plain,
connectTimeout: 30000,
receiveTimeout: 30000,
// ignore: missing_return
validateStatus: (code) {
if (code >= 200) {
return true;
}
}); static Dio dio = Dio(options);
In UI page i have to declare that uri variable and BaseOption variable in this future function:
Future<dynamic> _loginUser(String email, String password) async {
try {
Options options = Options(
headers: {"Content-Type": "application/json"},
);
Response response = await dio.post('/login',
data: {
"email": email,
"password": password,
"user_type": 2,
"status": 1
},
options: options);
if (response.statusCode == 200 || response.statusCode == 201) {
var responseJson = json.decode(response.data);
return responseJson;
} else if (response.statusCode == 401) {
throw Exception("Incorrect Email/Password");
} else
throw Exception('Authentication Error');
} on DioError catch (exception) {
if (exception == null ||
exception.toString().contains('SocketException')) {
throw Exception("Network Error");
} else if (exception.type == DioErrorType.RECEIVE_TIMEOUT ||
exception.type == DioErrorType.CONNECT_TIMEOUT) {
throw Exception(
"Could'nt connect, please ensure you have a stable network.");
} else {
return null;
}
}
}
You can create app_config.dart file and manage different environments like below:
const _baseUrl = "baseUrl";
enum Environment { dev, stage, prod }
Map<String, dynamic> _config;
void setEnvironment(Environment env) {
switch (env) {
case Environment.dev:
_config = devConstants;
break;
case Environment.stage:
_config = stageConstants;
break;
case Environment.prod:
_config = prodConstants;
break;
}
}
dynamic get apiBaseUrl {
return _config[_baseUrl];
}
Map<String, dynamic> devConstants = {
_baseUrl: "https://devapi.xyz.com/",
};
Map<String, dynamic> stageConstants = {
_baseUrl: "https://api.stage.com/",
};
Map<String, dynamic> prodConstants = {
_baseUrl: "https://api.production.com/",
};
Maybe instead of statically declaring your Dio object you could put it in a class, also put your loginUser function in there, and use Provider to obtain that object to call it where you need it.
class Api {
static var uri = "https://xxx/xxx/web_api/public";
static BaseOptions options = BaseOptions(
baseUrl: uri,
responseType: ResponseType.plain,
connectTimeout: 30000,
receiveTimeout: 30000,
// ignore: missing_return
validateStatus: (code) {
if (code >= 200) {
return true;
}
});
Dio dio = Dio(options);
Future<dynamic> loginUser(String email, String password) async {
try {
RequestOptions options = RequestOptions(
headers: {"Content-Type": "application/json"},
);
Response response = await dio.post('/login',
data: {
"email": email,
"password": password,
"user_type": 2,
"status": 1
},
options: options);
//the rest of your code here
}
https://pub.dev/packages/provider
Provider(
create: (_) => Api(),
child: ...
)
https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html
YourWidget(
child: Consumer<Api>(
builder: (context, api, child) {
return FutureBuilder<dynamic>(
future: api.loginUser('mail#mail.com', 'user_password')
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
//show a widget based on snapshot.data
} else {
//show another widget
}
}
},
),
)

Unable to access fetched data in initState in Flutter

I have class named Cards that has a method getCards that returns back a Future. I use this method to get cards from an endpoint.
Cards:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';
class Cards {
String _accessToken;
String _refreshToken;
List<dynamic> cardsId = List();
Future<dynamic> getCards() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
_accessToken = sharedPreferences.getString("access");
_refreshToken = sharedPreferences.getString("refresh");
var jsonData;
var response = await sendRequestToGetCards(
url: "http://10.0.2.2:8000/accounts/list/", accessToken: _accessToken);
if (response.statusCode == 200) {
jsonData = json.decode(utf8.decode(response.bodyBytes));
return jsonData;
} else if (response.statusCode == 401) {
_accessToken = await getNewAccessToken(_refreshToken);
response = await sendRequestToGetCards(
url: "http://10.0.2.2:8000/accounts/list/",
accessToken: _accessToken);
if (response.statusCode == 200) {
jsonData = json.decode(utf8.decode(response.bodyBytes));
return jsonData;
}
}
}
getNewAccessToken(String refreshToken) async {
var refreshResponse = await http.post(
"http://10.0.2.2:8000/users/api/token/refresh/",
body: {'refresh': refreshToken});
if (refreshResponse.statusCode == 200) {
var jsonData = json.decode(refreshResponse.body);
return jsonData['access'];
}
}
sendRequestToGetCards({String url, String accessToken}) async {
var response = await http.get(
url,
headers: {"Authorization": "Bearer $accessToken"},
);
return response;
}
}
I have an other class called CardData as my Provider data/state:
import 'package:flutter/material.dart';
import '../cards/cards.dart';
class CardData extends ChangeNotifier {
static Cards cards = Cards();
Future<dynamic> cardsList = cards.getCards;
}
Which you can see I created an object from Cards to access getCards that makes me able to access the returned Future and saving it in cardsList.
Now in my widget that I used to display all the cards I created a method called addToList to access the Provider data.
I've created some lists to save Widgets to pass them to other Widget later.
List<Widget> cardsList = List();
List<dynamic> cardsId = List();
List<Widget> nonCards = List();
List<dynamic> nonCardsId = List();
addToList() async {
var jsonData = await Provider.of<CardData>(context).cardsList;
for (var i = 0, len = jsonData.length; i < len; i++) {
if(jsonData[i]['account_type'] == "1") {
cardsList.add(
BankCard(
bankName: jsonData[i]['title'],
colors: [Color(0xFFD00E00), Color(0xFFF44336)],
cardNumber: jsonData[i]['number'],
cardDesc: jsonData[i]['description'],
),
);
cardsId.add(jsonData[i]['id']);
} else if(jsonData[i]['account_type'] == "2") {
nonCards.add(
NonBankCard(
bankName: jsonData[i]['title'],
colors: [Color(0xFFFF4B2B), Color(0xFFFDB76C)],
),
);
nonCardsId.add(jsonData[i]['id']);
}
}
}
But I need to use addToList method in initState as you know but I can't. when I do use it there the app screen will disappears.
you should initialize your list at the beginning
List<Widget> cardsList = new List<Widget>();
Call addToList inside your initState function:
#override
void initState() {
addToList();
}
at the end of addToList() put the updated list in setState()
setState(() {
cardsList = List.from(cardsList);
});

KnockoutJS + WebAPI 2 Token Authentication - maintain login state until token expires

I'm fairly new to token based authentication and I have a problem of how to maintain login state after I login.
I want to create a SPA website for which I am using Knockoutjs for my front end and SammyJS for routing and changing the views.
After I login in and get the token I store it in localStorage and set the username into an observable which I am displaying.
My problem is that after I close the tab or browser and I go back to the site, the token is in the localStorage but I can't see the user logged in.
I want to maintain the login state until the token expires. My question is what should I do with the token from the localStorage when I enter the site in order to maintain the login state of that user?
Do I need to make something in the startup class or to check if that user exists in the DB?
Thanks in advance!
Here is my code:
StartupAuth.cs
[assembly: OwinStartup(typeof(EventHub.PL.WebUI.Startup))] namespace EventHub.PL.WebUI {
public partial class Startup
{
public static OAuthAuthorizationServerOptions OAuthOptions { get;private set; }
public static OAuthBearerAuthenticationOptions OAuthBearerOptions { get; private set; }
public const string TokenEndpointPath = "/api/token";
public static string PublicClientId { get; private set; }
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString(TokenEndpointPath),
Provider = new ApplicationOAuthProvider(PublicClientId),
//AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
//app.UseOAuthBearerTokens( OAuthOptions );
app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
}
}
AccountController.cs
[HttpPost]
[AllowAnonymous]
[Route("Login")]
public async Task<IHttpActionResult> Login(LoginUser model)
{
var request = HttpContext.Current.Request;
var tokenServiceUrl = request.Url.GetLeftPart(UriPartial.Authority) + request.ApplicationPath + "/api/Token";
using (var client = new HttpClient())
{
var requestParams = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", model.Email),
new KeyValuePair<string, string>("password", model.Password)
};
var requestParamsFormUrlEncoded = new FormUrlEncodedContent(requestParams);
var tokenServiceResponse = await client.PostAsync(tokenServiceUrl, requestParamsFormUrlEncoded);
var responseString = await tokenServiceResponse.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<TokenResponse>(responseString);
var responseCode = tokenServiceResponse.StatusCode;
if (responseCode == HttpStatusCode.OK)
{
RegisterUser user = userRepository.GetNameById(json.Id);
var data = new
{
status = "success",
json.access_token,
user.Lastname
};
return Json(data);
}
return Json(new { status = "failed" });
}
}
here is the KO part:
var LoginApp = function () {
var instance = this;
instance.mainViewModel = new MainViewModel();
instance.loginViewModel = new LoginViewModel();
instance.loginRepository = new LoginRepository();
instance.loginViewModel.signIn = function() {
$('.loader-header').show();
var postData = {
email: instance.loginViewModel.email(),
password: instance.loginViewModel.password
}
instance.loginRepository.SignIn(SignInSuccess, postData);
};
instance.SignInSuccess = function(response) {
if (response.status === 'success') {
instance.mainViewModel.username(response.Lastname);
instance.mainViewModel.isVisible(true);
var userData = {
token: response.access_token,
username: response.Lastname
};
localStorage.setItem('AuthorizationData', JSON.stringify(userData));
$('.loader-header').hide();
dialog.close();
} else {
$('.loader-header').hide();
}
};
instance.init = function () {
ko.applyBindings(instance.loginViewModel, document.getElementById("signin-form"));
ko.applyBindings(instance.mainViewModel, document.getElementById("main-wrapper"));
}
instance.init();
}
$(document).ready(function () {
var loginApp = LoginApp();
});
UPDATE
here is my routing also
var appRoot = root;
(function ($) {
var app = $.sammy('#page', function () {
this.get('#/home', function (context) {
document.title = 'Home - ' + title;
var url = getUrlFromHash(context.path);
loadView(url, new MainViewModel(), MainApp);
//context.load(url).swap();
});
this.get('#/about', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
this.get('#/manage', function (context) {
var url = getUrlFromHash(context.path);
loadView(url, new AboutViewModel(), AboutApp);
});
});
$(function () {
app.run('#/home');
});
})(jQuery);
function loadView(url, viewModel, callback) {
$.get(url, function (response) {
var $container = $('#page');
//var $view = $('#page').html(response);
$container.html(response);
callback();
});
}
function getUrlFromHash(hash) {
var url = hash.replace('#/', '');
if (url === appRoot)
url = 'home';
return url;
}
Right now all you're doing is storing the user's credentials in localStorage but not using them to perform authorization. One alternative is to use the Sammy.OAuth2 plugin (which you can find it here).
You can define a route to make the authentication like:
app.post("#/oauth/login", function(context) {
this.load('http://yourwebsite/login',
{
cache: false,
type: 'post',
data: {
email: $("input[name=email]").val(),
password: $("input[name=password]").val()
}
})
.then(function(content) {
if(content != false){
if(app.getAccessToken() == null){
app.setAccessToken(token());
}
}else{
app.trigger("oauth.denied");
return false;
}
});
});
In 'protected' routes you can check if the user is already logged in like this:
app.get("#/profile", function(context) {
if(app.getAccessToken() != null)
context.render('view/profile.template');
else
this.requireOAuth();
});
This examples will have to be modified to populate the token according to your scenario. Here's a complete tutorial on Sammy.Oath2.