Authentication using Single-sign-on - web-services

In my web service project i need to include a authentication part using single-sign-on and spring. I am new to this technology. I was searching for an sample application for past few days, but i couldn't find it. Please help me by suggesting any tutorials. It will be a great help for me

For single-sign-on, the web service method for authentication should take username and password as parameters, and this authentication method should return a MAC ('Message Authentication Code', also named alternatively 'Authentication Token').
All the subsequent calls to other web service methods should contain this MAC so that the web service methods know which user is calling them.
//example pseudo code for authentication
string web_service_auth(string User_Name,string Password) {
...
string Mac;
if (access_accepted(User_Name,Password))
Mac = ...;
else
Mac = "access-denied";
return Mac;
}
//example pseudo code for other methods
string web_service_get_my_info(string Mac) {
if (!mac_is_valid(Mac))
return "invalid-mac";
...
string User_Name = mac_to_username(Mac);
string Info = get_user_info(User_Name);
return Info;
}

Related

Microsoft C++ Rest SDK for Graph APIs

I am trying out Microsoft's C++ Rest SDK (https://microsoft.github.io/cpprestsdk/index.html) to invoke Graph APIs but it has been a struggle so far.
In C# I can complete my tasks in a few lines of code. For example, refer following code from a Microsoft tutorial:
AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
bool isUsingClientSecret = AppUsesClientSecret(config);
IConfidentialClientApplication app;
if (isUsingClientSecret)
{
app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(config.Authority))
.Build();
}
string[] scopes = new string[] { $"{config.ApiUrl}.default" };
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
}
catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
{
}
// config.ApiUrl is set to "graph.microft.com"
if (result != null)
{
var httpClient = new HttpClient();
var apiCaller = new ProtectedApiCallHelper(httpClient);
await apiCaller.CallWebApiAndProcessResultASync($"{config.ApiUrl}v1.0/users", result.AccessToken, Display);
}
Now for cross-platform support, I need to develop similar functionality in C++ and for this purpose, we are exploring C++ Rest SDK from Microsoft. But I am unable to find any good examples to achieve a simple thing like providing client ID, client secret to get access token and to authorize.
Please let me know if anyone has come across any example / link to achieve the same.
Here you have some code for oauth 2.0 in Dropbox, Linkedin and MS Live scope:
https://github.com/microsoft/cpprestsdk/blob/master/Release/samples/Oauth2Client/Oauth2Client.cpp
Other samples within C++ Rest SDK:
https://github.com/microsoft/cpprestsdk/tree/master/Release/samples
First of all, you have to distinguish:
1. MS Graph authentication - which is, in fact, Azure Access Directory/Microsoft identity platform authentication, based on oauth 2.0 (short name: MSAL)
2. Accessing the MS Graph API using access token from the authentication process (in the standard process you should use MS Graph SDK)
For the C++ there is no MSAL or SDK library.
So - for authentication, you should use oauth 2.0 example which I pasted above.
Because you need to write everything on your own, please read deeply docs about authentication for MS Graph
https://learn.microsoft.com/en-us/graph/auth/
Here you can watch all the needed endpoints, secrets etc. for sample Postman calls:
https://learn.microsoft.com/en-us/graph/use-postman#set-up-on-behalf-of-api-calls
https://developer.microsoft.com/en-us/graph/blogs/30daysmsgraph-day-13-postman-to-make-microsoft-graph-calls/
In the URLs there are the following variables used:
Callback URL: https://app.getpostman.com/oauth2/callback
Auth URL: https://login.microsoftonline.com/**TENANTID**/oauth2/v2.0/authorize
Access Token URL: https://login.microsoftonline.com/**TENANTID**/oauth2/v2.0/token
Client ID: CLIENTID
Client Secret: CLIENTSECRET
Scope: https://graph.microsoft.com/.default
State: RANDOMSTRING
For the API calls, read about Microsoft Graph REST API v1.0 reference
https://learn.microsoft.com/en-us/graph/api/overview?toc=./ref/toc.json&view=graph-rest-1.0

How to secure restful webservice without using authentication

Can some one please let me know what are all the ways to secure restful web service written in spring boot project using spring rest(there is no user credentials check as this service is invoked by remote application sitting on different server)
Problem Statement:
I have a rest class and a method, which should be accessed by another remote application. Remote application will not send anything except body content and content-type. In this scenario how can I secure this rest service so that service can be accessible by only that particular remote application.
#RequestMapping("/rest")
#RestController
public class WorkflowController {
#RequestMapping(value = "ticket/create", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public Long startWorkflow(#RequestBody TicketInfo ticketInfo) {
...//DO SOMETHING
Long id = 1L;
return id; // return some long value
}
}
Please suggest what is the way to achieve this.
Thanks in advance
Ok so i dont know if i completely understand your question, but ill asume different scenarios.
Say your client application sits on a
static ip you could create a filter and a whitelist of ip addresses, that would be really simple, and probably not good enough.
If thats not the case you can use a parameter either GET or POST and again create a filter, you'll have to send the authentication string in your first call to get authentication. you'll also have to implement the authentication manager.
if(hsr.getParameter("ex_code") != null){
String exCode= hsr.getParameter("ex_code");
String userToken = new String( Base64.getDecoder().decode(hsr.getParameter("ex_code")));
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken(serviceThatReturnsAUserDetailsFacade.loadUserByUsername(userToken),
exCode);
token.setDetails(authenticationDetailsSource.buildDetails((HttpServletRequest) request));
try {
authentication = authenticationManager.authenticate(token);
....
If you do not want to implement any security & just would like to validate the host & port (only one app can run on a particular host & port) and assuming you using Spring then you can simply fetch following from incoming HttpServletRequest :-
a) RemoteAddr -> IP address of machine from which request originated.
b) RemoteHost -> Host name of machine from which request originated.
c) RemotePort -> Port of machine from which request originated.
Have one interfacing method in place which will validate this & if valid then allow it to go through while if invalid then return respective error msg to client.
Apart from this there is one other option also known as "Anonymous Authorization" with details here.

Editing SharePoint2010 List from Out side windows app.

Actually I am trying to add items in a share point List from a windows application. It all goes fine when I added the web reference and the able to get the all the offerings listed for Lists.asmx.
When I execute my program and try to call listServiceObj.GetListAndview("Customers","");
It gives me error "The request failed with HTTP status 401: Unauthorized" . Please note that at this time my credentials and url of the service reference were;
SpListService.Lists spListService = new SpListService.Lists();
spListService.Credentials = System.Net.CredentialCache.DefaultCredentials;
spListService.Url = "http://localhost/_vti_bin/Lists.asmx";
XmlNode customerListView = spListService.GetListAndView("Customers", "");
Then I Changed the above code to ;
SpListService.Lists spListService = new SpListService.Lists();
spListService.Credentials = System.Net.CredentialCache.DefaultCredentials;
spListService.Url = "http://<PC-Name>/sites/Home/_vti_bin/Lists.asmx";
XmlNode customerListView = spListService.GetListAndView("Customers", "");
then I recieved the following error;
"Exception of type 'Microsoft.SharePoint.SoapServer.SoapServerException' was thrown."
I have made the logged in user in the group of full controll. also the member of the Administrator group.. but same result....
Also please note that when try and access "http://localhost/" or "http:///" it gives me access denied page of SP2010.... instead I have to write "http:///sites/Home/SitePages/Home.aspx" to open my team site collection
I really stuck in to this .... would be really pleased to have some solution to this problem of mine...... Thanks in advance
MJay
I had a similar problem when I implemented my first SharePoint lists Web Service client. The reason was that the autogenerated client class actually introduced itself as a Mozilla web browser by default! The SharePoint server did not allow basic authentication for browsers so the client was actually redirected to firewall login page.
I suggest you to inherit another class from the Lists class and do the following:
Set another user agent value in constructor.
Set the "preauthenticate" property to true. This should force the client to send the credentials in the first request, not only after they have been asked for.
If necessary, try giving the credentials explicitly.
See the example below.
public class CustomizedLists : Lists
{
public CustomizedLists() : base()
{
this.UserAgent = "Some SharePoint client";
this.PreAuthenticate = true;
System.Net.ICredentials creds = new System.Net.NetworkCredential("user", "pwd");
this.Credentials = creds.GetCredential(uri, "Basic");
}
}

How to have a webmethod POST to an HTTPHandler.ashx file

Summary
How to create an HTTPContext within a webservice? or POST to a Handler.ashx from a webservice?
Background
I have a Cold Fusion web application that uses Forms authentication but somehow achieves Windows authentication with this script:
<cfscript>
ws = CreateObject("webservice", "#qTrim.webServiceName#");
ws.setUsername("#qTrim.trimAcct#");
ws.setPassword("#qTrim.trimpwd#");
wsString=ws.UploadFileCF("#qTrim.webserviceurl#","#objBinaryData#", "#qFiles.Filename#", "Document", "#MetaData#");
</cfscript>
Apparently, the setUsername/setPassword values map to a single Windows domain account and this works in production. (The webservice is written in C# and built with .Net 4.0. and it must be used by this domain account)
I developed a DownloadHandler.ashx which works when POSTed to by a process which is running under this domain account (I have a .Net web client with a button that defines PostBackUrl="~/DownloadHandler.ashx"). This HTTPHandler grabs a few items from the HTTPContext and then calls the above webservice method DownloadFile without problems.
My Problem
Now this ColdFusion app needs to download a file using this webservice. When the CF code POSTs an HTML form to the DownloadHandler.ashx it works - BUT ONLY IF the CF tester is using this Windows domain account. This won't work in production because the CF app supports remote anonymous users through forms authentication.
Question
Not knowing ColdFusion myself, I was thinking of the following changes:
Replicate the above CF technique such that user/pswd can be set the same and have CF invoke the ws.DownloadFile method directly
I think this would require using most of my current HTTPHandler code in my webservice but I cannot think of how to handle the output. When this handler is POSTed to, it prompts for OPEN or Save and works nicely but I'm confused on how I would stream this back from the webservice itself.
The current DownloadFile webmethod communicates with a database product and returns output to this (the current) handler:
Code
namespace WebClient
{
public class DownloadHandler : IHttpHandler
{
ASMXproxy.FileService brokerService;
public void ProcessRequest(HttpContext context)
{
brokerService = new ASMXproxy.FileService();
string recNumber = context.Request.Form["txtRecordNumber"];
brokerService.Url = context.Request.Form["txtURL"];
string trimURL = context.Request.Form["txtFakeURLParm"]; // not a real URL but parms to connect to TRIM
brokerService.Timeout = 9999999;
brokerService.Credentials = System.Net.CredentialCache.DefaultCredentials;
byte[] docContent;
string fileType;
string fileName;
string msgInfo = brokerService.DownloadFile(trimURL, recNumber, out docContent, out fileType, out fileName);
string ContentType = MIMEType.MimeType(fileType);
context.Response.AppendHeader("Content-Length", docContent.Length.ToString());
context.Response.AppendHeader("content-disposition", "attachment; filename=\"" + fileName + "\"");
context.Response.ContentType = ContentType;
context.Response.OutputStream.Write(docContent, 0, docContent.Length);
context.Response.OutputStream.Flush();
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Assuming your CF site is running on IIS and not Apache or some other web server, this might work:
Put your .cfm file that calls the webservice into its own subfolder on your site. Set the Authentication properties of that folder to use Anonymous Authentication, but set the user identity to the Windows Domain account that successfully calls the webservice (click the Set... button on the dialog shown below and enter the appropriate credentials).

Login failed when a web service tries to communicate with SharePoint 2007

I created a very simple webservice in ASP.NET 2.0 to query a list in SharePoint 2007 like this:
namespace WebService1
{
/// <summary>
/// Summary description for Service1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// 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 Service1 : System.Web.Services.WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World";
}
[WebMethod]
public string ShowSPMyList()
{
string username = this.User.Identity.Name;
return GetList();
}
private string GetList()
{
string resutl = "";
SPSite siteCollection = new SPSite("http://localhost:89");
using (SPWeb web = siteCollection.OpenWeb())
{
SPList mylist = web.Lists["MySPList"];
SPQuery query = new SPQuery();
query.Query = "<Where><Eq><FieldRef Name=\"AssignedTo\"/><Value Type=\"Text\">Ramprasad</Value></Eq></Where>";
SPListItemCollection items = mylist.GetItems(query);
foreach (SPListItem item in items)
{
resutl = resutl + SPEncode.HtmlEncode(item["Title"].ToString());
}
}
return resutl;
}
}
}
This web service runs well when tested using the built-in server of Visual Studio 2008. The username indicates exactly my domain account (domain\myusername).
However when I create a virtual folder to host and launch this web service (still located in the same machine with SP2007), I got the following error when invoking ShowSPMyList() method, at the line to execute OpenWeb(). These are the details of the error:
System.Data.SqlClient.SqlException: Cannot open database "WSS_Content_8887ac57951146a290ca134778ddc3f8" requested by the login. The login failed.
Login failed for user 'NT AUTHORITY\NETWORK SERVICE'.
Does anyone have any idea why this error happens? Why does the web service run fine inside Visual Studio 2008, but not when running stand-alone? I checked and in both cases, the username variable has the same value (domain\myusername).
Thank you very much.
Thank you very much for the replies. I'll look into the documents to see how i can change the settings related to the application pool as suggested.
I want to make clear that i wanted to build a webservice to run outside of sharepoint (but can be deployed on the same server with sharepoint).
Is there any way i can programmatically pass the credentials (another domain account instead of 'NT AUTHORITY\NETWORK SERVICE' by default) to sharepoint when invoking OpenWeb method? I believe if i'm able to do that then i can walkaround the security issue above.
When you create your own custom virtual folder and set it inside the IIS, it's highly possible that the user account who run the application pool of that particular IIS virtual directory is currently set to NT authority\Network Service.
You can check carefully, by looking closely of what is the actual application pool that run that particular IIS virtual directory.
From there, you can go to the "Application Pool" folder and right click, choose Properties. Select the "Identity" tab, and it will show you who is the user account that currently running the application pool.
Alternatively, you can refer to the SharePoint SDK, something similar to ExtractCrmAuthenticationToken in dynamics CRM to extract the Authentication Token ticket.
Or alternatively you can use Network Credential to embed your own custom user id and password.
Hope this helps,
hadi teo
I fully agree with Hadi, if this is something you want to just quickly test, for a proof of concept, you can change the credentials under what the Application pool runs, to a user that has permissions. Or you could use Identity Impersonate setting in your config file.
However resist the temptiation to do this in a production enviroment, use the proper authentication. It will come back, to bite you.
If you need to set this up for production, there is a couple of areas that you want to look at, duplicate SPN's, and deligation probably the most common areas that is not configured correctly. Your error however points to impersanation not happening.
Also make sure you are deploying the web service to its own web site that does not already run SharePoint. If you want the web service to run on the same web site as SharePoint read Creating a Custom Web Service.
You can check what application pool identity SharePoint is using by following the same instructions that Hadi writes, but for an app pool running SharePoint. Make sure to only change the application pool used by your web service and not SharePoint or else other permission errors could occur within SP. (There should be no reason but if you are interested in changing the app pool identity used by SharePoint follow these instructions.)
On solution would be to "impersonate" as the SharePoint System account using the following code:
SPSecurity.RunWithElevatedPrivileges(delegate()
{
// also dispose SPSite
using (SPSite siteCollection = new SPSite("http://localhost:89"))
{
using (SPWeb web = siteCollection.OpenWeb())
{
// ...
}
}
});