I have an webapi that will live on a corporate network and will only have windows authenticated users.
I am trying to authenticate HttpContext.Current.Request.LogonUserIdentity.Name directly because HttpContext.Current.Request.LogonUserIdentity.IsAuthenticated returns false.
I am doing it this way to avoid the user login popup for non-admin users.
using System;
using System.Diagnostics;
using System.Web.Http;
using System.Web;
using System.Web.Http.Controllers;
namespace myAPI.Helpers
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeCustomAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
// HttpContext.Current.User.Identity.Name is always empty at this point
// and must authenticate first with HandleUnauthorizedRequest(actionContext)
// but that pops up an annoying login screen,
// HttpContext.Current.Request.LogonUserIdentity.Name has the value I need
// but it is not authenticated which raises some security concerns
// Check against a list of admins
if (HttpContext.Current.Request.LogonUserIdentity.IsAuthenticated && Authentication.IsAdmin( HttpContext.Current.Request.LogonUserIdentity.Name ))
{
Debug.WriteLine("USER IS AUTHORIZED");
}
else
{
Debug.WriteLine("USER IS DENIED");
//HandleUnauthorizedRequest(actionContext); // This will popup a login unless it is overridden
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK); // return a blank response instead
}
}
}
}
This was my simplest solution:
Only check authentication of known admins
Redirect admins that are not authenticated
Non-admins won't get a login popup
using System;
using System.Diagnostics;
using System.Web.Http;
using System.Web;
using System.Web.Http.Controllers;
namespace myAPI.Helpers
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AuthorizeCustomAttribute : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
// Check against a list of admins
if (Authentication.IsAdmin(HttpContext.Current.User.Identity.Name) || Authentication.IsAdmin( HttpContext.Current.Request.LogonUserIdentity.Name ))
{
if(HttpContext.Current.User.Identity.IsAuthenticated || HttpContext.Current.Request.LogonUserIdentity.IsAuthenticated )
{
Debug.WriteLine("USER IS AUTHORIZED");
} else
{
Debug.WriteLine("USER IS AN ADMIN BUT IS UNAUTHENTICATED");
HandleUnauthorizedRequest(actionContext); // redirect to get authenticated
}
}
else
{
Debug.WriteLine("USER IS NOT AN ADMIN AND IS DENIED");
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.OK); // return a blank response
}
}
}
}
I'm trying to create a handler that will produce a popup when a message is received from SignalR which is running in a service. I've got this working in a non-service, but it won't work in a service.
This code works from a non-service:
client.OnMessageReceived +=
(sender2, message) =>
RunOnUiThread(() =>
showMessage(message));
Where client is the SignalR client and showMessage is the method called when a message is received from client. No problem.
Now I want to run the client in/as a service and I need to wire up a handler to do basically the same thing. I've tried several methods I found on StackOverflow and other sites, but all the stuff out there is java, not c# for Xamarin for Visual Studio (2017) and does not translate well. I'm at a loss as to how to proceed.
* Update *
This is my forground service code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Acr.UserDialogs;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Support.V4.App;
using Android.Views;
using Android.Widget;
using ChatClient.Shared;
using Java.Lang;
namespace OML_Android
{
public class SignalRService : Service
{
public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
public const string ACTION_MAIN_ACTIVITY = "OML_Android.action.MainActivity";
public const string SERVICE_STARTED_KEY = "has_service_been_started";
bool isStarted;
Handler handler;
Action runnable;
// This information will eventually be pulled from the intent, this is just for testing
public string firstname = "";
public string lastname = "";
public string username = "";
public string name = "";
private Client mInstance;
public override IBinder OnBind(Intent intent)
{
return null;
// throw new NotImplementedException();
}
public override void OnCreate()
{
base.OnCreate();
mInstance = Client.Getinstance(name, username, firstname, lastname);
mInstance.Connect();
}
public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
{
return StartCommandResult.Sticky;
}
public override void OnDestroy()
{
// Not sure what to do here yet, got to get the service working first
}
void RegisterForegroundService()
{
var notification = new NotificationCompat.Builder(this)
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentText(Resources.GetString(Resource.String.notification_text))
.SetSmallIcon(Resource.Drawable.alert_box)
.SetContentIntent(BuildIntentToShowMainActivity())
.SetOngoing(true)
//.AddAction(BuildRestartTimerAction())
//.AddAction(BuildStopServiceAction())
.Build();
// Enlist this instance of the service as a foreground service
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
}
PendingIntent BuildIntentToShowMainActivity()
{
var notificationIntent = new Intent(this, typeof(MainActivity));
notificationIntent.SetAction(ACTION_MAIN_ACTIVITY);
notificationIntent.SetFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTask);
notificationIntent.PutExtra(SERVICE_STARTED_KEY, true);
var pendingIntent = PendingIntent.GetActivity(this, 0, notificationIntent, PendingIntentFlags.UpdateCurrent);
return pendingIntent;
}
public async void showMessage(string message)
{
var result = await UserDialogs.Instance.ConfirmAsync(new ConfirmConfig
{
Message = "Text Message from Company: " + System.Environment.NewLine + message,
OkText = "Ok",
});
if (result)
{
// do something
var x = message;
}
}
}
}
This service sets the client to run as a foreground service (I assume), the client code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR.Client;
namespace ChatClient.Shared
{
public sealed class Client
{
//public string username;
private string username = "";
private string _platform = "";
private readonly HubConnection _connection;
private readonly IHubProxy _proxy;
public string _Username
{
get { return username; }
set { username = value; }
}
public string _Platform
{
get { return _platform; }
set { _platform = value; }
}
public event EventHandler<string> OnMessageReceived;
public static Client instance = null;
public Client(string name, string username, string firstname, string lastname, string company, string department, string section)
{
_Username = username;
_Platform = name;
_platform = _Platform;
Dictionary<string, string> queryString = new Dictionary<string, string>();
queryString.Add("username", username);
queryString.Add("firstname", firstname);
queryString.Add("lastname", lastname);
queryString.Add("company", company);
queryString.Add("department", department);
queryString.Add("section", section);
_connection = new HubConnection("https://www.example.com/SignalRhub",queryString );
_proxy = _connection.CreateHubProxy("chathub");
}
public static Client Getinstance(string name, string username, string firstname, string lastname)
{
// create the instance only if the instance is null
if (instance == null)
{
// The username and user's name are set before instantiation
instance = new Client(name, username, firstname, lastname,"","","");
}
// Otherwise return the already existing instance
return instance;
}
public async Task Connect()
{
await _connection.Start(); //.ContinueWith._connection.server.registerMeAs("");
_proxy.On("broadcastMessage", (string platform, string message) =>
{
if (OnMessageReceived != null)
OnMessageReceived(this, string.Format("{0}: {1}", platform, message));
});
// Send("Connected");
}
public async Task<List<string>> ConnectedUsers()
{
List<string> Users = await _proxy.Invoke<List<string>>("getConnectedUsers");
return Users;
}
public async Task<List<string>> ConnectedSectionUsers(string company, string department, string section, string username)
{
List<string> Users = await _proxy.Invoke<List<string>>("getConnectedSectionUsers",company, department, section, username);
return Users;
}
public Task Send(string message)
{
return _proxy.Invoke("Send", _platform, message);
}
public Task SendSectionMessage(string company, string department, string section, string name, string message)
{
return _proxy.Invoke("messageSection", company, department, section, name, message);
}
public Task SendCompanyMessage(string company, string department, string section, string name, string message)
{
return _proxy.Invoke("messageCompany", company, name, message);
}
}
}
This is the code I plan to use to start the service (not working yet), I will be adding information to the intent via intent.PutExtras, namely Firstname, lastname, username, name, company, department and section. For now I just set them to null string in the service for testing purposes.:
public static void StartForegroundServiceComapt<SignalRService>(this Context context, Bundle args = null) where SignalRService : Service
{
var intent = new Intent(context, typeof(SignalRService));
if (args != null)
{
intent.PutExtras(args);
}
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
context.StartForegroundService(intent);
}
else
{
context.StartService(intent);
}
}
This all works as expected, I still need to do some work to clean it up but it is working. I am able to connect to the server hub and send messages to the correct groups of people. Now I need to get this to run as a foreground service.
Is your app creating a notification channel? You should be passing the notification channel ID to the NotificationCompat.Builder constructor.
It doesn't look like you're calling the RegisterForegroundService method to promote the service to a foreground service. You'll want to call RegisterForegroundService early in the OnCreate override. Modern versions of Android require a foreground service to show a notification within a few seconds or an exception will be thrown.
You may want to add the android.permission.FOREGROUND_SERVICE permission to your Android Manifest because it's required on Android P and later.
I don't think ACR.UserDialogs will work if your app has no current top activity. The service outlives the activity so it's very possible to run into this scenario. You can simply have the service update the existing foreground notification to show the user a new message is available.
I have a Web API with only one user, I'm trying to use the basic authentication to protect it but it always returns code 401 unauthorized.
This is my code:
Class BasicAuthenticationAttribute
using System;
using System.Threading;
using System.Security.Principal;
using System.Text;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using System.Net;
using System.Net.Http;
public class BasicAuthenticationAttribute: AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext.Request.Headers.Authorization == null)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
else
{
// Gets header parameters
string authenticationString = actionContext.Request.Headers.Authorization.Parameter;
string originalString = Encoding.UTF8.GetString(Convert.FromBase64String(authenticationString));
// Gets username and password
string usrename = originalString.Split(':')[0];
string password = originalString.Split(':')[1];
// Validate username and password
if (!CheckUser.Login(usrename, password))
{
// returns unauthorized error
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
base.OnAuthorization(actionContext);
}
}
Class Checkuser
using System;
public class CheckUser
{
public static bool Login(string username, string password)
{
if (username == "user" && password == "mypassword")
return true;
else
return false;
}
}
The API Controller
public class adduserController : ApiController
{
[HttpGet, BasicAuthentication]
[Route("api/user/{email}")]
public string adduser(string email)
{
string country_code = "";
string username = System.Threading.Thread.CurrentPrincipal.Identity.Name;
return "Welcome";
}
This is my JQuery function:
var token = '';
var headers = {};
if (token) {
headers.Authorization = 'Basic YWhdZWQer5WhtZWRAMjAxNw==';
}
$.ajax({
type: 'GET',
url: 'http://mywebapi.com',
headers: headers
}).done(function (data) {
self.result(data);
})
I don't know what is wrong with my code!! Please help
Thank you in advance
You create an empty token, then you set your headers if that token is not empty, so you never set any headers...
var token = '';
var headers = {};
if (token) {
headers.Authorization = 'Basic YWhdZWQer5WhtZWRAMjAxNw==';
}
Try
if (!token) {
For a assignment, I was trying to send a post request with a given token(no user and password values, I just had API token), this did not work for me :
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic",
"converted base 64 token / api token ");
This help me to be authenticate, and solved my problem :
client.DefaultRequestHeaders.Add("Authorization", "Basic JEcYcEMyantZV095WVc3G2JtVjNZbWx1");
I have created a web service in Visual Studio 2010 using the wsdl provided by our vendor. I have created an interface using their wsdl and implemented that interface for our implementation. They also need a SSL certificate so we have provided them certificate open ssl of windows. Web service also implements authentication using soap header.
But they are unable to connect and getting the following error
<soap:Body>
<soap:Fault>
<faultcode>soap:MustUnderstand</faultcode>
<faultstring>SOAP header Security was not understood.</faultstring>
</soap:Fault>
</soap:Body>
My web service code is as follows for your reference
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Data;
using System.Data.SqlClient;
using System.Web.Services.Protocols;
using System.IO;
using Microsoft.Web.Services3;
namespace MyWebService1
{
/// <summary>
/// Summary description for PSWebService
/// </summary>
///
[WebService(Namespace = "http://tempuri.org")]
[WebServiceBinding(Name = "PSWebService", ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class PSWebService : Microsoft.Web.Services3.WebServicesClientProtocol, IPartStoreRequiredServiceSOAP11Binding
{
public UserCredentials consumer;
Functions objFunctions = new Functions ();
String SqlConnStr = "Data Source=test\\test;uid=sa;pwd=abc123;database=testdb";
public AeslPSWebService()
{
}
[WebMethod]
[SoapDocumentMethod(Binding = "PSWebService")]
[SoapHeader("consumer", Direction = SoapHeaderDirection.In, Required = true)]
public CustomerInformationOutput getCustomerInformation(CustomerInformationInput custLookUpInput)
{
CustomerInformationOutput cio = new CustomerInformationOutput();
try
{
if (checkUser())
{
// My business logic goes here
}
}
catch (Exception ex)
{
}
}
private bool checkUser()
{
// In this method you can check the username and password
// with your database or something
// You could also encrypt the password for more security
if (consumer != null)
{
if (consumer.Username == "sa" && consumer.Password == "abc123")
return true;
else
return false;
}
else
return false;
}
}
# region "SOAP Headers"
public class UserCredentials : System.Web.Services.Protocols.SoapHeader
{
public string Username;
public string Password;
}
# endregion
}
Please help as i am unable to resolve it.
I already used SOAP for getting the Idnumber from the database of ASp.net C# and I created an App that he/she can log using Email and Password and will go to next activity. This is the code that is used for trying to logged in using the Email and Password in WebSite.
public class MainActivity extends Activity {
EditText Email, Password;
Button button;
TextView error;
String EmailAddfromWeb, PassfromWeb;
private static final String SOAP_ACTION = "http://tempuri.org/findContact";
private static final String OPERATION_NAME = "CheckLogin";
private static final String WSDL_TARGET_NAMESPACE = "http://tempuri.org/";
private static final String SOAP_ADDRESS = "http://10.0.2.2:64485/WebService.asmx";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Email = (EditText)findViewById(R.id.editText1);
Password = (EditText)findViewById(R.id.editText2);
button = (Button)findViewById(R.id.button1);
error = (TextView)findViewById(R.id.textView3);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v){
SoapObject request = new SoapObject(WSDL_TARGET_NAMESPACE, OPERATION_NAME);
PropertyInfo propertyInfo = new PropertyInfo();
propertyInfo.type = PropertyInfo.STRING_CLASS;
propertyInfo.name = "EmailAdd";
EmailAddfromWeb = Email.getText().toString();
request.addProperty(propertyInfo, EmailAddfromWeb);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE httpTransport = new HttpTransportSE(SOAP_ADDRESS);
try
{
httpTransport.call(SOAP_ACTION, envelope);
Object response = envelope.getResponse();
error.setText(response.toString());
SPLPVIEWER s = new SPLPVIEWER();
Intent intent = new Intent(MainActivity.this, SPLPVIEWER.class);
Bundle b = new Bundle();
b.putString(s.holder, "" + String.valueOf(Email));
intent.putExtras(b);
startActivity(intent);
finish();
}
catch(Exception e)
{
error.setText("Invalid Email!");
}
}
});
Code for WebService.asmx.cs :
How can i compare my data login in Web Service from the inputted in AndroidApplication.
using System;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Linq;
using System.Data.SqlClient;
using System.Data;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WebService : System.Web.Services.WebService
{
public WebService()
{
}
[WebMethod]
public bool CheckLogin(string EmailAdd, string Password)
{
return getEmail(EmailAdd, Password);
}
public bool getEmail(String EmailAdd, String Password)
{
SqlConnection conn;
conn = ConnectionManager.GetConnection();
conn.Open();
bool check = false;
string pa;
SqlCommand CheckCmd = new SqlCommand("CheckEmailAddress", conn);
CheckCmd.CommandType = CommandType.StoredProcedure;
CheckCmd.Parameters.Add("#EmailAddress", SqlDbType.NVarChar).Value = EmailAdd;
SqlDataReader sdr = CheckCmd.ExecuteReader();
while (sdr.Read())
{
pa = sdr.GetString(8); // for Password column
if (pa == Password)
{
check = true;
}
break;
}
conn.Close();
return check;
}
}
use asp Web service or simple post methods.
Try this http://www.androidhive.info/2011/11/android-xml-parsing-tutorial/
Build your xml page via asp.net.