Request.Cookies["cookieName"] is null in web service method - c#

This is how I'am saving cookie in my code which is a web service method. I tried to change the cookie expire time but still it is not working for me. Is there any issue with Context.Response to write cookie or Context.Request to read it??
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = false)]
public void SaveInDraft(FormRootObject _forms)
{
HttpCookie formCookie = new HttpCookie("UserData");
formCookie.Values.Add("formid", _forms.formid);
foreach (var item in _forms.fielddata)
{
formCookie.Values.Add(item.id, item.value);
}
formCookie.Expires = DateTime.Now.AddYears(1);
Context.Response.Cookies.Add(formCookie);
}
for retrieving this cookie on next page
[WebMethod]
[ScriptMethod(ResponseFormat = ResponseFormat.Json, UseHttpGet = false)]
public FormRootObject getDataFromCookie()
{
FormRootObject form = new FormRootObject();
List<formFielddata> lstFormFieldData = new List<formFielddata>();
var getCookie=Context.Request.Cookies["UserData"];
if (getCookie != null)
{
if (getCookie.Values.Count > 0)
{
foreach (var val in getCookie.Values)
{
formFielddata document = new formFielddata();
if (val.ToString() != "formid")
{
document.id = val.ToString();
document.value = getCookie[val.ToString()];
lstFormFieldData.Add(document);
}
else {
form.formid = getCookie[val.ToString()];
}
}
form.fielddata = lstFormFieldData;
}
}
return form;
}
but my object getCookie is always null

Rajesh, forms are only present on web applications. If you're using plain web api to create your service, no form is going to be presented to your service client.
To create a cookie:
var resp = new HttpResponseMessage();
var cookie = new CookieHeaderValue("session-id", "12345");
cookie.Expires = DateTimeOffset.Now.AddDays(1);
cookie.Domain = Request.RequestUri.Host;
cookie.Path = "/";
resp.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return resp;
To consume cookie:
CookieHeaderValue cookie = Request.Headers.GetCookies("session-id")

I got this by storing cookies as
HttpContext.Current.Response.Cookies.Add(formCookie);
And reading this as
var getCookie=HttpContext.Current.Request.Cookies["UserData"];

Related

How to update token using refresh token in MVC Client Application?

There are two type of application in my solution
1)Web api application
2)MMC c# application
Here I created web api application which has facility of token authentication.
In this Application username and password validating from SQL server database.
i.e If any user request for web api token that user detail must be present in database table.(In user Table Id,Username,Password column are there with data).
So my web api application connected to database server.
Now I created MVC c# application which consume web api and access the data.
what I do here that when user put credential to mvc app login screen and that credential goes to api and validate them.
Api will give response of data If user credential are correct.
Here I got JSON response from web api and data like "access_token","Expire_time","refresh_token" etc
I stored all these detail in Session object.
So whenever I request for Getdata() from mvc app I passing 'access_token' to api and retuned result data.
I set web api token timeout 2 minutes.(token get deleted after 2 minutes)
So problem goes here that how I can maintain user login session in web api using refresh_token.I do not want to user again get login screen and come back to that screen.
Because every 2 minutes he will get login screen which is not correct solution.
I want some function when api get timeout access_token and mvc application again call refresh_token and continue data transaction.
Whenever your accesstoken is expired you can pass refresh token and can update the access token like this. Hope this will help you.
[AllowAnonymous]
[HttpPost]
public IHttpActionResult GetAccessToken(RefreshTokenModel getRefreshToken)
{
ApiResponse apiResponse = new ApiResponse();
apiResponse.Message = "Your session has expired. Kindly login again.";
try
{
var getHashToken = GenerateHash.GetHash(getRefreshToken.RefreshToken);
var getRefreshTokenDetails = tokenDetailBl.GetRefreshTokenDetail(getHashToken);
if (getRefreshTokenDetails != null && getRefreshTokenDetails.ExpiresUtc > DateTime.UtcNow && !string.IsNullOrEmpty(getRefreshTokenDetails.ProtectedTicket))
{
if (getRefreshTokenDetails.DeviceType == getRefreshToken.DeviceType)
{
var currentTime = DateTime.UtcNow;
var refreshTokenLifeTime = Convert.ToDouble(ConfigurationManager.AppSettings["RefreshTokenExpireTime"]);
var tokenExpiration = Convert.ToDouble(ConfigurationManager.AppSettings["AccessTokenExpireTime"]);
ApiIdentityManager apiIdentityManager = new ApiIdentityManager();
var tokenData = JsonConvert.SerializeObject(new { Ticket = getRefreshTokenDetails.ProtectedTicket, DeviceId = getRefreshTokenDetails.DeviceId });
var getIdentityToken = apiIdentityManager.GetRefreshToken(tokenData);
// Delete Old Tokens
tokenDetailBl.DeleteAccessTokenByDevice(getRefreshTokenDetails.DeviceId);
var refreshToken = new RefreshToken()
{
RefreshTokenId = GenerateHash.GetHash(getIdentityToken.RefreshToken),
DeviceId = getRefreshTokenDetails.DeviceId,
DeviceType = getRefreshToken.DeviceType,
UserId = getRefreshTokenDetails.UserId,
IssuedUtc = currentTime,
ExpiresUtc = currentTime.AddMinutes(Convert.ToDouble(refreshTokenLifeTime)),
ProtectedTicket = getIdentityToken.Ticket
};
//Save new tokens
tokenDetailBl.SaveAccessToken(new TokenDetail
{
AccessToken = getIdentityToken.AccessToken,
CreatedOn = DateTime.UtcNow,
UserId = getRefreshTokenDetails.UserId,
DeviceId = getRefreshTokenDetails.DeviceId,
DeviceType = getRefreshToken.DeviceType
});
tokenDetailBl.SaveRefreshToken(refreshToken);
//Get token cache.
CachedData cachedData = new CachedData(tokenDetailBl);
var getAllToken = cachedData.GetAccessTokens();
cachedData.UpdateTokenCache(getIdentityToken.AccessToken, getRefreshTokenDetails.UserId + ":" + DateTime.UtcNow.AddMinutes(tokenExpiration).ToFormateDateTimeString());
var getUserDetails = userBl.GetUserDetails(getRefreshToken.UserId);
getUserDetails.DeviceId = getRefreshTokenDetails.DeviceId;
getUserDetails.DeviceType = getRefreshTokenDetails.DeviceType;
getUserDetails.AccessToken = getIdentityToken.AccessToken;
getUserDetails.TokenType = "bearer";
getUserDetails.ExpiresIn = getIdentityToken.ExpiresIn;
getUserDetails.Issued = getIdentityToken.Issued;
getUserDetails.Expires = DateTime.UtcNow.Add(TimeSpan.FromMinutes(tokenExpiration)).ToString("R");
getUserDetails.RefreshToken = getIdentityToken.RefreshToken;
//Dictionary<string, string> tokenResponse = new Dictionary<string, string>();
//tokenResponse.Add("access_token", getIdentityToken.AccessToken);
//tokenResponse.Add("token_type", "bearer");
//tokenResponse.Add("expires_in", getIdentityToken.ExpiresIn);
//tokenResponse.Add("issued", getIdentityToken.Issued);
//tokenResponse.Add("expires", DateTime.UtcNow.Add(TimeSpan.FromMinutes(tokenExpiration)).ToString("R"));
//tokenResponse.Add("refresh_token", getIdentityToken.RefreshToken);
return ResponseMessage(Request.CreateResponse(HttpStatusCode.OK, getUserDetails));
}
else
{
apiResponse.Message = "Your session has expired. Kindly login again.";
}
}
}
catch (Exception ex)
{
Logger.Error(ex);
}
return ResponseMessage(Request.CreateResponse(HttpStatusCode.Gone, apiResponse));
}
You can use MVC filters to check that your access token is expired or not something like this.
[CacheAuthorize]
[HttpPost]
public IHttpActionResult GetUserList(SearchRequest searchRequest)
and after that code to check validation of access token
public class CacheAuthorizeAttribute : AuthorizeAttribute
{
public CacheAuthorizeAttribute(params string[] roles)
: base()
{
Roles = string.Join(",", roles);
}
public override void OnAuthorization(HttpActionContext actionContext)
{
Dictionary<HttpStatusCode, string> response;
if (SkipAuthorization(actionContext))
{
return;
}
var userSessionManager = new UserCacheManager();
if (userSessionManager.ReValidateSession(out response))
{
base.OnAuthorization(actionContext);
}
else
{
ApiResponse apiResponse = new ApiResponse(response.Values.FirstOrDefault());
actionContext.Response = actionContext.ControllerContext.Request.CreateResponse(response.Keys.FirstOrDefault(), apiResponse);
}
}
/// <summary>
/// Re-validates the user session. Usually called at each authorization request.
/// If the session is not expired, extends it lifetime and returns true.
/// If the session is expired or does not exist, return false.
/// </summary>
/// <returns>true if the session is valid</returns>
public bool ReValidateSession(out Dictionary<HttpStatusCode, string> errorResponse)
{
errorResponse = new Dictionary<HttpStatusCode, string>();
string authToken = this.GetCurrentBearerAuthrorizationToken();
ITokenDetailRepository tokenDetailRepository = new TokenDetailRepository();
ITokenDetailBL tokenDetailBl = new TokenDetailBL(tokenDetailRepository);
CachedData cachedData = new CachedData(tokenDetailBl);
if (!string.IsNullOrEmpty(authToken))
{
var currentUserId = this.GetCurrentUserId();
var getUserTokens = cachedData.GetAccessTokens();
if (!getUserTokens.ContainsKey(authToken))
{
//Get Data from DB
cachedData.GetAccessToken(authToken);
getUserTokens = cachedData.GetAccessTokens();
}
return CheckAccessToken(getUserTokens, authToken, out errorResponse);
}
else
{
errorResponse.Add(HttpStatusCode.Gone, "Access token not found.");
}
return false;
}
private bool CheckAccessToken(Dictionary<string, string> accessTokenDictionary, string authToken, out Dictionary<HttpStatusCode, string> errorResponse)
{
errorResponse = new Dictionary<HttpStatusCode, string>();
var hasToken = accessTokenDictionary.ContainsKey(authToken);
if (hasToken)
{
var getTokenValue = accessTokenDictionary[authToken];
var enCulture = new CultureInfo("en-US");
DateTime tokenAddedDate;
var isCorrectDate = DateTime.TryParseExact(getTokenValue.Split(new char[] { ':' }, 2)[1], "dd-MMM-yyyy,hh:mm tt", enCulture, DateTimeStyles.None, out tokenAddedDate);
if (isCorrectDate)
{
if (tokenAddedDate >= DateTime.UtcNow)
{
return true;
}
else
{
//Check Refresh token expired or not
errorResponse.Add(HttpStatusCode.Unauthorized, "Access token expired.");
}
}
else
{
errorResponse.Add(HttpStatusCode.Gone, "Invalid access token.");
}
}
else
{
errorResponse.Add(HttpStatusCode.Gone, "Invalid access token.");
}
return false;
}

Jira REST API returning HTML

I am trying to access the Jira rest API via C#. For that I am using the Windows.Web.Http.HttpClient. But all I get as a return value is HTML.
I am calling the following URL: https://jira.atlassian.com/rest/api/latest/field/
Little Edit
When I call the url from a browser it works fine, just the call from the HttpClient doesn't work.
Here is my code:
public async Task<IRestResponse> Execute(RestRequest request) {
var restResponse = new RestResponse();
var client = new HttpClient();
var req = new HttpRequestMessage(request.Method, new Uri(BaseUrl, UriKind.RelativeOrAbsolute));
foreach (var item in request.headers) {
req.Headers[item.Key] = item.Value;
}
req.Headers.Accept.Add(new HttpMediaTypeWithQualityHeaderValue("application/json"));
if (this.Authenticator != null)
req.Headers["Authorization"] = this.Authenticator.GetHeader();
var res = await client.SendRequestAsync(req);
restResponse.Content = await res.Content.ReadAsStringAsync();
restResponse.StatusCode = res.StatusCode;
restResponse.StatusDescription = res.ReasonPhrase;
if (!res.IsSuccessStatusCode) {
restResponse.ErrorMessage = restResponse.Content;
restResponse.ResponseStatus = ResponseStatus.Error;
} else if (res.StatusCode == HttpStatusCode.RequestTimeout) {
restResponse.ResponseStatus = ResponseStatus.TimedOut;
} else if (res.StatusCode == HttpStatusCode.None) {
restResponse.ResponseStatus = ResponseStatus.None;
} else {
restResponse.ResponseStatus = ResponseStatus.Completed;
}
return restResponse;
}
I just found my problem, I don't add the relative path anywhere. It just calls the BaseUrl meaning https://jira.atlassian.com/ that explains why I get the HTML page.

Federated Authentication in SharePoint 2013 ( with ADFS) - Operation Timeout when getting FedAuth cookie

We are encountering a bit of an odd issue on our integration code with SharePoint.
We've managed to get the integration working using SAML tokens, see code below.
The problem is that sometimes it times out when getting the FedAuth cookie or making the request to SharePoint.
The timeouts might point to load or network issues but they are reproducible.
The first unit test on the unit test suite gets the cookie without any problem every single time, but it fails on the second.
To make this more mystifying, it seems to work fine whenever I have fiddler capturing traffic.
Even more annoying is the fact that if i run the second unit test and not the first, the test works fine.
It's as if SharePoint refuses to issue another cookie for the same client until a certain amount of time has passed, unless going through Fiddler.
I should add, that I have tried both storing the cookie for subsequent requests and getting it again for each request, it seems to make no difference.
Any help would be appreciated.
public static ClientContext CreateClientContext(SharePointClaimsConnection connection)
{
if (connection == null)
{
throw new ArgumentNullException("connection");
}
logger.DebugFormat("Create Client Context for connection: {0}", connection);
ClientContext context = new ClientContext(connection.WebUrl);
try
{
if (connection.SecurityTokenServiceEndPoint != null && !String.IsNullOrEmpty(connection.Realm))
{
CookieInfo token = GetToken(connection);
if (token == null)
{
lock (syncRoot)
{
token = GetToken(connection);
if (token == null)
{
token = GetFedAuthCookie(connection);
if (token != null)
{
tokens[connection] = token;
}
}
}
}
if (token != null)
{
context.ExecutingWebRequest += (s, e) =>
{
e.WebRequestExecutor.WebRequest.KeepAlive = true;
for (int i = 0; i < e.WebRequestExecutor.WebRequest.Headers.Count; i++)
{
string key = e.WebRequestExecutor.WebRequest.Headers.GetKey(i);
string value = e.WebRequestExecutor.WebRequest.Headers.Get(i);
logger.DebugFormat("Key: {0}, Value: {1}", key, value);
}
CookieContainer container = new CookieContainer();
foreach (var cookie in token.Cookies)
{
logger.Debug("Adding cookie: " + cookie.Name);
logger.Debug("Domain: " + connection.WebUrl.Host);
logger.Debug("Expires: " + cookie.Expires.ToString());
Cookie newCookie = new Cookie(cookie.Name, cookie.Value);
newCookie.Expires = DateTime.MaxValue;
newCookie.Path = "/";
newCookie.Secure = true;
newCookie.HttpOnly = true;
newCookie.Domain = connection.WebUrl.Host;
container.Add(newCookie);
}
e.WebRequestExecutor.WebRequest.CookieContainer = container;
};
}
}
return context;
}
catch (Exception ex)
{
if (context != null)
{
context.Dispose();
}
throw;
}
}
private static CookieInfo GetFedAuthCookie(SharePointClaimsConnection connection)
{
string result = GetSamlToken(connection);
//Take this token and pass it to SharePoint STS
string stringData = String.Format(CultureInfo.InvariantCulture, "wa=wsignin1.0&wctx={0}&wresult={1}",
HttpUtility.UrlEncode(new Uri(connection.WebUrl, "/_layouts/Authenticate.aspx?Source=%2F").ToString()),
HttpUtility.UrlEncode(result));
HttpWebRequest sharepointRequest = HttpWebRequest.Create(new Uri(connection.WebUrl, "/_trust/")) as HttpWebRequest;
sharepointRequest.Method = "POST";
sharepointRequest.ContentType = "application/x-www-form-urlencoded";
sharepointRequest.CookieContainer = new CookieContainer();
sharepointRequest.AllowAutoRedirect = false; // This is important
using (Stream newStream = sharepointRequest.GetRequestStream())
{
byte[] data = Encoding.UTF8.GetBytes(stringData);
newStream.Write(data, 0, data.Length);
}
HttpWebResponse webResponse = sharepointRequest.GetResponse() as HttpWebResponse;
if (webResponse.Cookies["FedAuth"] == null)
{
return null;
}
return new CookieInfo()
{
Cookies = webResponse.Cookies.Cast<Cookie>().ToList(),
};
}
private static string GetSamlToken(SharePointClaimsConnection connection)
{
string result;
Uri STSService = new Uri(connection.SecurityTokenServiceEndPoint, WindowsTransport);
using (WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(
new WindowsWSTrustBinding(SecurityMode.Transport),
new EndpointAddress(STSService)))
{
trustChannelFactory.TrustVersion = System.ServiceModel.Security.TrustVersion.WSTrust13;
trustChannelFactory.Credentials.SupportInteractive = false;
trustChannelFactory.ConfigureChannelFactory<IWSTrustChannelContract>();
//Request Security Token
RequestSecurityToken rst = new RequestSecurityToken();
rst.KeyType = KeyTypes.Bearer;
rst.RequestType = RequestTypes.Issue;
rst.AppliesTo = new EndpointAddress(connection.Realm);
var channel = trustChannelFactory.CreateChannel();
WSTrust13RequestSerializer trustSerializer = new WSTrust13RequestSerializer();
using (Message message = Message.CreateMessage(
MessageVersion.Default, WSTrust13Constants.Actions.Issue,
new RequestBodyWriter(trustSerializer, rst)))
{
Message response2 = channel.EndIssue(channel.BeginIssue(message, null, null));
XmlDictionaryReader reader = response2.GetReaderAtBodyContents();
result = reader.ReadOuterXml();
}
}
return result;
}

ASP.NET MVC 5 - Cookies being not created

I am having my controller action that calls a private method that handles a cookie. The problem is that the cookie is not being created at all. I read multiple posts on SO but I haven't found an answer as I think that my handling cookies in this code is correct.
Is there any web.config setting that I need to check in regards to cookies? I also tried different browsers.
I debugged the code and I can see that the cookie is actually set in the code but as soon as I load the page and have a look at cookies the cookie is not there.
private ABHomeModel HandleWhiteBoxCookie(ABHomeModel model)
{
var whiteBox = _whiteBoxService.GetActiveWhiteBox();
if (model.WhiteBox != null)
{
const string cookieName = "whiteBox";
var whiteBoxCookie = HttpContext.Request.Cookies.Get(cookieName);
if (whiteBoxCookie != null)
{
var displayedTimes = Convert.ToInt32(whiteBoxCookie.Value);
if (displayedTimes < 2)
{
displayedTimes++;
var cookie = new HttpCookie(cookieName, displayedTimes.ToString())
{
Expires = new DateTime().AddMonths(1),
Secure = false
};
HttpContext.Response.Cookies.Set(cookie);
ViewBag.IsWhiteBoxActive = true;
}
else
{
ViewBag.IsWhiteBoxActive = false;
}
}
else
{
var cookie = new HttpCookie(cookieName, "1")
{
HttpOnly = true,
Domain = Request.Url.Host,
Expires = DateTime.Now.AddMonths(1),
Secure = false
};
HttpContext.Response.Cookies.Add(cookie);
ViewBag.IsWhiteBoxActive = true;
}
model.WhiteBox = whiteBox;
}
return model;
}
My colleague found the issue. It is in regard to setting the domain. As soon as we removed this line:
Domain = Request.Url.Host,
The cookies started working and are now being created.
The full updated code of the method:
private ABHomeModel HandleWhiteBoxCookie(ABHomeModel model)
{
var whiteBox = _whiteBoxService.GetActiveWhiteBox();
if (whiteBox != null)
{
const string cookieName = "whiteBox";
var whiteBoxCookie = HttpContext.Request.Cookies.Get(cookieName);
if (whiteBoxCookie != null)
{
var displayedTimes = Convert.ToInt32(whiteBoxCookie.Value);
if (displayedTimes < 2)
{
displayedTimes++;
var cookie = new HttpCookie(cookieName, displayedTimes.ToString())
{
HttpOnly = true,
Secure = false
};
HttpContext.Response.Cookies.Set(cookie);
ViewBag.IsWhiteBoxActive = true;
}
else
{
ViewBag.IsWhiteBoxActive = false;
}
}
else
{
var cookie = new HttpCookie(cookieName, "1")
{
HttpOnly = true,
Expires = DateTime.Now.AddMonths(1),
Secure = false
};
HttpContext.Response.Cookies.Add(cookie);
ViewBag.IsWhiteBoxActive = true;
}
model.WhiteBox = whiteBox;
}
return model;
}

Including SAML2.0 token in WCF service call without using WIF

I'm trying to set up a WCF service protected by ADFS. I'm currently able to request a token and send it with the request using WIF and Thinktecture IdentityModel 4.5 with the following code:
static SecurityToken GetToken()
{
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
"https://fs2.server2012.local/adfs/services/trust/13/usernamemixed")
{
TrustVersion = TrustVersion.WSTrust13
};
if (factory.Credentials != null)
{
factory.Credentials.UserName.UserName = #"username";
factory.Credentials.UserName.Password = "password";
}
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Symmetric,
AppliesTo = new EndpointReference(
"https://wcfservicecertificate/wcfservice/Service.svc/wstrust"),
};
var channel = factory.CreateChannel();
RequestSecurityTokenResponse rstr;
return channel.Issue(rst, out rstr);
}
With this I can call the WCF service by using ChannelFactory.CreateChannelWithIssuedToken:
var factory = new ChannelFactory<IService>(binding,
new EndpointAddress("https://wcfservicecertificate/wcfservice/Service.svc/wstrust"));
if (factory.Credentials != null)
{
factory.Credentials.SupportInteractive = false;
factory.Credentials.UseIdentityConfiguration = true;
}
var proxy = factory.CreateChannelWithIssuedToken(GetToken());
var result= proxy.GetData(2);
This works as expected but can only be used on (mobile) windows platforms. I would also like to be able to use the same principle on iOS and Android. Using this article I was able to request a security token from ADFS using the following code:
const string soapMessage =
#"<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope""
xmlns:a=""http://www.w3.org/2005/08/addressing""
xmlns:u=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"">
<s:Header>
<a:Action s:mustUnderstand=""1"">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>
<a:To s:mustUnderstand=""1"">https://fs2.server2012.local/adfs/services/trust/13/UsernameMixed</a:To>
<o:Security s:mustUnderstand=""1"" xmlns:o=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">
<o:UsernameToken u:Id=""uuid-6a13a244-dac6-42c1-84c5-cbb345b0c4c4-1"">
<o:Username>username</o:Username>
<o:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"">password</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<trust:RequestSecurityToken xmlns:trust=""http://docs.oasis-open.org/ws-sx/ws-trust/200512"">
<wsp:AppliesTo xmlns:wsp=""http://schemas.xmlsoap.org/ws/2004/09/policy"">
<a:EndpointReference>
<a:Address>https://wcfservicecertificate/wcfservice/Service.svc/wstrust</a:Address>
</a:EndpointReference>
</wsp:AppliesTo>
<trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>
<trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
<trust:TokenType>urn:oasis:names:tc:SAML:2.0:assertion</trust:TokenType>
</trust:RequestSecurityToken>
</s:Body>
</s:Envelope>";
var webClient = new WebClient();
webClient.Headers.Add("Content-Type", "application/soap+xml; charset=utf-8");
var result = webClient.UploadString(
address: "https://fs2.server2012.local/adfs/services/trust/13/UsernameMixed",
method: "POST",
data: soapMessage);
This results in a SAML2.0 token which I would like to send in a request to our WCF service in order to authenticate. There are various sources (including the article mentioned earlier) which state that this should be possible but I've yet to find a solution.
Any help would be appreciated.
You can use one of hybrid solutions which use SAML with OAuth or other authorization technologies. This is more secure against phising techniques. For SAML only approach, you can refer to following link: How to pass security tokenfrom one wcf service to another wcf service. It is said that you need to enable saveBootstrapTokens property on webconfig.
This link can be useful too: Availability of Bootstrap Tokens
This can easily be done without using WIF. Lets completely avoid WIF and the .Net framework and do it in Java for illustration purposes. First make a call to the Security Token Service using the template approach like you have done. You then need to extract the SAML from the response, Base64 encode it and stuff it in the Autorization header of the subsequent request to your protected WCF service. You may also need to do the same with a ProofKey if you are coding for Non-Repudiation. Also I'm only showing authentication using username/password for brevity as Certificate Authentication involves much more work - you have to hash (SHA1 )part of the message then encrypt the hash with the private key of the cert and then add this as a xml element to the original message etc...
Here is the java helper code:
import java.io.*;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.time.Instant;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.HttpsURLConnection;
public class SecurityService {
private String _username;
private String _password;
private String _stsUrl;
private String _samlAssertion;
private String _samlEncoded;
private String _binarySecret;
private String _workingDirectory;
private String _platformUrl;
private String _soapBody;
private Integer _responseCode;
private Integer _plaformResponseCode;
private String _response;
private String _platformResponse;
private String _xproofSignature;
private Map<String, String> _headerDictionary;
public void setUsername(String username) {
this._username = username;
}
public void setPassword(String password) {
this._password = password;
}
public void setStsUrl(String stsUrl) {
this._stsUrl = stsUrl;
}
public String getStsUrl() {
return _stsUrl;
}
public void setplatformUrl(String platformUrl) {
this._platformUrl = platformUrl;
}
public String getSamlAssertion() {
return _samlAssertion;
}
public String getSamlEncoded() {
return _samlEncoded;
}
public String getSoapBody() {
return _soapBody;
}
public Integer getResponseCode() {
return _responseCode;
}
public Integer getPlatformResponseCode() {
return _plaformResponseCode;
}
public String getResponse() {
return _response;
}
public String getPlatformResponse() {
return _platformResponse;
}
public String getXProofSignature() {
return _xproofSignature;
}
public String getBinarySecret() {
return _binarySecret;
}
public String gePlatFormUrl() {
return _platformUrl;
}
public void setHeaderDictionary(Map<String, String> headerDictionary){
this._headerDictionary = headerDictionary;
}
public Map<String, String> getHeaderDictionary(){
return _headerDictionary;
}
public SecurityService() throws Exception {
}
public SecurityService(Boolean useConfig) throws Exception {
if (useConfig) {
this._workingDirectory = System.getProperty("user.dir") + "\\app.config";
this.getProperties();
}
}
public void sendAuthenticatedGet() throws Exception {
URL obj = new URL(_platformUrl);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// optional default is GET
con.setRequestMethod("GET");
// Add request header
con.setRequestProperty("Authorization", "Saml " + _samlEncoded);
con.setRequestProperty("X-ProofSignature", _xproofSignature);
_plaformResponseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
_platformResponse = response.toString();
}
public void sendAuthenticatedPost(String body) throws Exception {
URL obj = new URL(_platformUrl);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
//add request header
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
// Add request header
con.setRequestProperty("Authorization", "Saml " + _samlEncoded);
con.setRequestProperty("X-ProofSignature", _xproofSignature);
// Add Azure Subscription Key using generic Add Headers method
if (_headerDictionary != null) {
for (String key : _headerDictionary.keySet()) {
con.setRequestProperty(key, _headerDictionary.get(key));
}
}
_soapBody = body;
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
//wr.writeBytes(urlParameters);
wr.writeBytes(_soapBody);
wr.flush();
wr.close();
_responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
_response = response.toString();
}
// HTTP POST request
public void sendPostToSts() throws Exception {
URL obj = new URL(_stsUrl);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
//add request header
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/soap+xml");
String body = getTemplateCertificate();
_soapBody = (((body.replace("[Created]", Instant.now().toString())).replace("[Expires]", Instant.now()
.plusSeconds(300).toString())).replace("[username]", _username)).replace("[password]", _password).replace("[stsUrl]", _stsUrl);
// Send post request
con.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
//wr.writeBytes(urlParameters);
wr.writeBytes(_soapBody);
wr.flush();
wr.close();
_responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();
_response = response.toString();
// Get Binary Secret
// <trust:BinarySecret></trust:BinarySecret>
final Pattern patternBinarySecret = Pattern.compile("<trust:BinarySecret>(.+?)</trust:BinarySecret>");
final Matcher matcherBinarySecret = patternBinarySecret.matcher(response.toString());
matcherBinarySecret.find();
_binarySecret = matcherBinarySecret.group(1);
// Get the SAML Assertion
final Pattern patternEncryptedAssertion = Pattern.compile("<trust:RequestedSecurityToken>(.+?)</trust:RequestedSecurityToken>");
final Matcher matcherEncryptedAssertion = patternEncryptedAssertion.matcher(response.toString());
matcherEncryptedAssertion.find();
_samlAssertion = matcherEncryptedAssertion.group(1);
byte[] proofKeyBytes = _binarySecret.getBytes("UTF-8");
String encoded = Base64.getEncoder().encodeToString(proofKeyBytes);
byte[] decoded = Base64.getDecoder().decode(encoded);
// SAML Stuff - Works beautifully
byte[] samlBytes = _samlAssertion.getBytes("UTF-8");
_samlEncoded = Base64.getEncoder().encodeToString(samlBytes);
_xproofSignature = this.encode(_samlAssertion, _binarySecret);
}
private static String readFile( String file ) throws IOException {
BufferedReader reader = new BufferedReader( new FileReader(file));
String line = null;
StringBuilder stringBuilder = new StringBuilder();
String ls = System.getProperty("line.separator");
try {
while( ( line = reader.readLine() ) != null ) {
stringBuilder.append( line );
stringBuilder.append( ls );
}
return stringBuilder.toString();
} finally {
reader.close();
}
}
// Embedded WS-Trust template for username/password RST
private static String getTemplate () {
return "<s:Envelope xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:a=\"http://www.w3.org/2005/08/addressing\" xmlns:u= \"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"><s:Header><a:Action s:mustUnderstand= \"1\">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action><a:MessageID>urn:uuid:cfea5555-248c-46c3-9b4d- 54936b7f815c</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=\"1\">[stsUrl]</a:To><o:Security s:mustUnderstand=\"1\" xmlns:o=\"http://docs.oasis- open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"><u:Timestamp u:Id=\"_0\"><u:Created>[Created] </u:Created><u:Expires>[Expires]</u:Expires></u:Timestamp><o:UsernameToken u:Id=\"uuid-e273c018-1da7-466e-8671-86f6bfe7ce3c- 17\"><o:Username>[username]</o:Username><o:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username- token-profile-1.0#PasswordText\">[password] </o:Password></o:UsernameToken></o:Security></s:Header><s:Body><trust:RequestSecurityToken xmlns:trust=\"http://docs.oasis- open.org/ws-sx/ws-trust/200512\"><wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy \"><wsa:EndpointReference xmlns:wsa=\"http://www.w3.org/2005/08/addressing \"><wsa:Address>https://mbplatform/</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><trust:RequestType>http://docs.oasis- open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType><trust:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token- profile-1.1#SAMLV2.0</trust:TokenType></trust:RequestSecurityToken></s:Body></s:Envelope>";
}
private String encode(String key, String data) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
return Base64.getEncoder().encodeToString(sha256_HMAC.doFinal(data.getBytes("UTF-8")));
}
private void getProperties() throws Exception {
Properties prop = new Properties();
String fileName = _workingDirectory;
InputStream is = new FileInputStream(fileName);
prop.load(is);
_username = prop.getProperty("app.username");
_password = prop.getProperty("app.password");
_platformUrl = prop.getProperty("app.platformUrl");
_stsUrl = prop.getProperty("app.stsUrl");
}
}
and here is example usage:
SecurityService mbss = new SecurityService(true);
mbss.sendPostToSts();
System.out.println("CONTACTING AZURE SECURITY TOKEN SERVICE");
System.out.println("\nSending 'POST' request to URL : " + mbss.getStsUrl());
System.out.println("\nPost parameters : \n" + mbss.getSoapBody());
System.out.println("\nResponse Code : " + mbss.getResponseCode());
System.out.println("\nHERE IS THE SAML RESPONSE\n");
System.out.println(mbss.getResponse());
System.out.println("\nHERE IS THE BINARY SECRET\n");
System.out.println(mbss.getBinarySecret());
System.out.println("\nHERE IS THE SAML ASSERTION\n");
System.out.println(mbss.getSamlAssertion());
System.out.println("\nHERE IS THE ENCODED SAML ASSERTION\n");
System.out.println(mbss.getSamlEncoded());
System.out.println("\nHERE IS THE X-PROOF SIGNATURE\n");
System.out.println(mbss.getXProofSignature());
System.out.println("\nNOW CONTACTING WCF SERVICES WITH SECURITY HEADER\n");
mbss.sendAuthenticatedGet();
System.out.println("\nSending 'GET' request to URL : " + mbss.gePlatFormUrl());
System.out.println("Response Code : " + mbss.getPlatformResponseCode());
System.out.println("\nHERE ARE THE RESULTS FOLKS...ENJOY\n");
System.out.println(mbss.getPlatformResponse());

Categories