I am able to call the API to get the Token in my local, However how do i use this token in my ASP.NET MVC front end application and get the claims. I tried something like this (mentioned below) but some how i am not able to decrypt the token and get the claims. I made sure that Machine key’s are same.
var unencryptedToken = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(accessToken);
//(This line is always returning the null)
var identity = unencryptedToken.Identity;
var id = new ClaimsIdentity(identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
Can you help me with this??
I have used below article to build my webapi to generate the token.
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
Regards,
Rupesh
Though old question, but I had to implement this today. You can try getting information from the RequestContext's User object like this:
var userName = idt.Claims.Where(x => x.Type == "UserName").Select(x => x.Value).First();
var roles = idt.Claims.Where(x => x.Type == "role").Select(x => x.Value).FirstOrDefault();
return new[] {$"'userNameClaim':'{userName}','rolesClaim':'{roles}'"};
I have added extension method to make it easier to return comma separated string of roles:
public static class RequestExtensions
{
public static string GetRoles(this HttpRequestContext context)
{
var idt = context.Principal.Identity as ClaimsIdentity;
if (idt == null)
return string.Empty;
var roles = idt.Claims.Where(x => x.Type == "role")
.Select(x => x.Value).FirstOrDefault();
return roles;
}
public static string GetUserName(this HttpRequestContext context)
{
var idt = context.Principal.Identity as ClaimsIdentity;
if (idt == null)
return string.Empty;
var userName = idt.Claims.Where(x => x.Type == "UserName")
.Select(x => x.Value).FirstOrDefault();
return userName;
}
}
Related
I would like to consume my organizations dynamics oData endpoint but with early bound classes. However, there are a lot of early bound tools out there and I wanted to know which one provides the best developer experience/least resistance?
For example, there is this one:
https://github.com/daryllabar/DLaB.Xrm.XrmToolBoxTools
https://github.com/yagasoft/DynamicsCrm-CodeGenerator
and so on. Is there a developer preference/method out there?
Early bound classes are for use with the Organization Service which is a SOAP service. The normal way to generate those classes is using CrmSvcUtil.
OData can be used in Organization Data Service or Web API, but those don't have Early Bound classes.
Further reading: Introducing the Microsoft Dynamics 365 web services
It's not impossible to use with standard SOAP Early bound class. We just have to be creative. If we work just with basic attributes (fields, not relationships, ecc) it seems possible. For example. for create and update, OData will not accept the entire early bounded class, just pass the attibutes:
class Program
{
static void Main(string[] args)
{
string token = System.Threading.Tasks.Task.Run(() => GetToken()).Result;
CRMWebAPI dynamicsWebAPI = new CRMWebAPI("https:/ORG.api.crm4.dynamics.com/api/data/v9.1/",
token);
CRMGetListOptions listOptions = new CRMGetListOptions
{
Select = new string[] { "EntitySetName" },
Filter = "LogicalName eq 'contact'"
};
dynamic entityDefinitions = dynamicsWebAPI.GetList<ExpandoObject>("EntityDefinitions", listOptions).Result;
Contact contact = new Contact
{
FirstName = "Felipe",
LastName = "Test",
MobilePhone = "38421254"
};
dynamic ret = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.Create(entityDefinitions.List[0].EntitySetName, KeyPairValueToObject(contact.Attributes))).Result;
}
public static async Task<string> GetToken()
{
string api = "https://ORG.api.crm4.dynamics.com/";
ClientCredential credential = new ClientCredential("CLIENT_ID", "CLIENT_SECRET");
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/commom/oauth2/authorize");
return authenticationContext.AcquireTokenAsync(api, credential).Result.AccessToken;
}
public static object KeyPairValueToObject(AttributeCollection keyValuePairs)
{
dynamic expando = new ExpandoObject();
var obj = expando as IDictionary<string, object>;
foreach (var keyValuePair in keyValuePairs)
obj.Add(keyValuePair.Key, keyValuePair.Value);
return obj;
}
}
It's a simple approach and I didn't went further.
Maybe we have to serealize other objects as OptionSets, DateTime (pass just the string) and EntityReferences but this simple test worked fine to me. I'm using Xrm.Tools.WebAPI and Microsoft.IdentityModel.Clients.ActiveDirectory. Maybe it's a way.
[Edit]
And so I decided to go and created a not well tested method to cast the attributes. Problems: We have to follow OData statments to use the API. To update/create an entity reference we can use this to reference https://www.inogic.com/blog/2016/02/set-values-of-all-data-types-using-web-api-in-dynamics-crm/
So
//To EntityReference
entityToUpdateOrCreate["FIELD_SCHEMA_NAME#odata.bind"] = "/ENTITY_SET_NAME(GUID)";
So, it's the Schema name, not field name. If you use CamelCase when set you fields name you'll have a problem where. We can resolve that with a (to that cute) code
public static object EntityToObject<T>(T entity) where T : Entity
{
dynamic expando = new ExpandoObject();
var obj = expando as IDictionary<string, object>;
foreach (var keyValuePair in entity.Attributes)
{
obj.Add(GetFieldName(entity, keyValuePair), CastEntityAttibutesValueOnDynamicObject(keyValuePair.Value));
}
return obj;
}
public static object CastEntityAttibutesValueOnDynamicObject(object attributeValue)
{
if (attributeValue.GetType().Name == "EntityReference")
{
CRMGetListOptions listOptions = new CRMGetListOptions
{
Select = new string[] { "EntitySetName" },
Filter = $"LogicalName eq '{((EntityReference)attributeValue).LogicalName}'"
};
dynamic entitySetName = dynamicsWebAPI.GetList<ExpandoObject>("EntityDefinitions", listOptions).Result.List[0];
return $"/{entitySetName.EntitySetName}({((EntityReference)attributeValue).Id})";
}
else if (attributeValue.GetType().Name == "OptionSetValue")
{
return ((OptionSetValue)attributeValue).Value;
}
else if (attributeValue.GetType().Name == "DateTime")
{
return ((DateTime)attributeValue).ToString("yyyy-MM-dd");
}
else if (attributeValue.GetType().Name == "Money")
{
return ((Money)attributeValue).Value;
}
else if (attributeValue.GetType().Name == "AliasedValue")
{
return CastEntityAttibutesValueOnDynamicObject(((AliasedValue)attributeValue).Value);
}
else
{
return attributeValue;
}
}
public static string GetFieldName<T>(T entity, KeyValuePair<string, object> keyValuePair) where T : Entity
{
switch (keyValuePair.Value.GetType().Name)
{
case "EntityReference":
var entityNameList = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.GetEntityDisplayNameList()).Result;
var firstEntity = entityNameList.Where(x => x.LogicalName == entity.LogicalName).FirstOrDefault();
var attrNameList = System.Threading.Tasks.Task.Run(async () => await dynamicsWebAPI.GetAttributeDisplayNameList(firstEntity.MetadataId)).Result;
return attrNameList.Where(x => x.LogicalName == keyValuePair.Key).Single().SchemaName + "#odata.bind";
case "ActivityParty":
throw new NotImplementedException(); //TODO
default:
return keyValuePair.Key;
}
}
Please, note that this approach do not seems fast or good in anyway. It's better if you have all this values as static so we can save some fetches
[Edit 2]
I just found on XRMToolBox a plugin called "Early bound generator for Web API" and it seems to be the best option. Maybe you should give it a try if you're still curious about that. I guess its the best approach.
The final code is this:
static void Main(string[] args)
{
string token = Task.Run(() => GetToken()).Result;
dynamicsWebAPI = new CRMWebAPI("https://ORG.api.crm4.dynamics.com/api/data/v9.1/",
token);
Contact contact = new Contact
{
FirstName = "Felipe",
LastName = "Test",
MobilePhone = "38421254",
new_Salutation = new EntityReference(new_salutation.EntitySetName, new Guid("{BFA27540-7BB9-E611-80EE-FC15B4281C8C}")),
BirthDate = new DateTime(1993, 04, 14),
};
dynamic ret = Task.Run(async () => await dynamicsWebAPI.Create(Contact.EntitySetName, contact.ToExpandoObject())).Result;
Contact createdContact = dynamicsWebAPI.Get<Contact>(Contact.EntitySetName, ret, new CRMGetListOptions
{
Select = new string[] { "*" }
}).Result;
}
and you have to change the ToExpandoObject on Entity.cs class (generated by the plugin)
public ExpandoObject ToExpandoObject()
{
dynamic expando = new ExpandoObject();
var expandoObject = expando as IDictionary<string, object>;
foreach (var attributes in Attributes)
{
if (attributes.Key == GetIdAttribute())
{
continue;
}
var value = attributes.Value;
var key = attributes.Key;
if (value is EntityReference entityReference)
{
value = $"/{entityReference.EntitySetName}({entityReference.EntityId})";
}
else
{
key = key.ToLower();
if (value is DateTime dateTimeValue)
{
var propertyForAttribute = GetPublicInstanceProperties().FirstOrDefault(x =>
x.Name.Equals(key, StringComparison.InvariantCultureIgnoreCase));
if (propertyForAttribute != null)
{
var onlyDateAttr = propertyForAttribute.GetCustomAttribute<OnlyDateAttribute>();
if (onlyDateAttr != null)
{
value = dateTimeValue.ToString(OnlyDateAttribute.Format);
}
}
}
}
expandoObject.Add(key, value);
}
return (ExpandoObject)expandoObject;
}
Links:
https://github.com/davidyack/Xrm.Tools.CRMWebAPI
https://www.xrmtoolbox.com/plugins/crm.webApi.earlyBoundGenerator/
We currently use XrmToolkit which has it's own version of early binding called ProxyClasses but will allow you to generate early binding using the CRM Service Utility (CrmSvcUtil). It does a lot more than just early binding which is why we use it on all of our projects but the early binding features alone would have me sold on it. in order to regenerate an entity definition all you do is right click the cs file in visual studio and select regenerate and it is done in a few seconds.
For my first 3 years of CRM development I used the XrmToolbox "Early Bound Generator" plugin which is really helpful as well.
I can't figure out why when I try to connect from Xamarin Context.User.Indetity.Name is empty. Is there anything special I need to do? I logged in to the server and the user has a connection stablished. After that I use the following code:
var Connection = new HubConnection(Url);
_hub = Connection.CreateHubProxy(hubName);
_hub.On(srvEvent, onData);
await Connection.Start();
But I never get the username. What am I doing wrong?
Here's the code for the server:
var name = Context.User.Identity.Name;
Connections.Add(name, Context.ConnectionId);
return base.OnConnected();
It works when it comes from the web app, not from the xamarin app.
Thanks!
Here is the code I was telling you about.
I'm using an external OAuth2 server for authentication, so I must pass the access token to SignalR somehow, because SignalR uses web sockets for the messages back and forth I can't pass the access token in the header because this is not supported by web sockets.
I'm passing that access token as a query string parameter this way (Javascript client)
$.connection.hub.qs = "access_token=" + mytoken;
Then on my SignalR I added a middleware that takes that query string and adds it to the header as an Authorization header using Bearer Token. This is done this way in my startup class
app.UseAuthQSTokenExtractor();
The code for the middleware is this one
namespace Owin
{
public static class AuthorizationQSTokenExtractorExtension
{
public static void UseAuthQSTokenExtractor(this IAppBuilder app)
{
app.Use<AuthorizationQsTokenExtractorMiddleware>();
}
}
}
namespace Chat.Middleware
{
public class AuthorizationQsTokenExtractorMiddleware : OwinMiddleware
{
public AuthorizationQsTokenExtractorMiddleware(OwinMiddleware next)
: base(next)
{
}
public override async Task Invoke(IOwinContext context)
{
Debug.WriteLine("signalr-auth-middleware");
string bearerToken = context.Request.Query.Get("access_token");
Debug.WriteLine("signar-bearer: " + bearerToken);
if (bearerToken != null)
{
TokenHelper.DecodeAndWrite(bearerToken);
string[] authorization = { "Bearer " + bearerToken };
context.Request.Headers.Add("Authorization", authorization);
}
await Next.Invoke(context);
}
}
My startup class then looks like this
app.UseCors(CorsOptions.AllowAll);
app.UseAuthQSTokenExtractor();
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
var hubConfiguration = new HubConfiguration ();
hubConfiguration.EnableDetailedErrors = true;
app.RunSignalR(hubConfiguration);
You can see in the code above where I tell SignalR to use the Oauth2 Server, that code is this one
app.UseIdentityServerBearerTokenAuthentication(
new IdentityServerBearerTokenAuthenticationOptions
{
Authority = ConfigurationManager.AppSettings["api:idserver"],
RequiredScopes = new[]
{
"chat-hub"
}
});
After all this is set up I have access to my Context.User.Identity.Name and if you want to get the others IdentityClaim you can do this
var identity = Context.User.Identity as ClaimsIdentity;
Which I'm using that code above to get the subjectId (userid) like this
public static string[] GetIdentityClaimsIssSub(HubCallerContext Context)
{
var identity = Context.User.Identity as ClaimsIdentity;
if (identity == null)
return null;
var issuerFromIdentity = identity.FindFirst("iss");
var subFromIdentity = identity.FindFirst("sub");
if (issuerFromIdentity == null || subFromIdentity == null)
return null;
return new string[] { issuerFromIdentity.Value, subFromIdentity.Value };
}
I hope it helps
I've been learning how to implement CustomRoleProvider from a few tutorials and managed to implement 2 main methods as below
public override string[] GetRolesForUser(string userName)
{
string connectionString =
ConfigurationManager.ConnectionStrings["myDb"].ConnectionString;
DataContext context = new DataContext(connectionString);
Table<UserObj> usersTable = context.GetTable<UserObj>();
UserObj userObj = usersTable.SingleOrDefault(u => u.UserName == userName);
string roleId = userObj.UserRoleID;
if (roleId != null)
return roleId.Select(c => c.ToString()).ToArray();
else
return new string[] { };
}
public override bool IsUserInRole(string userName, string roleName)
{
string connectionString =
ConfigurationManager.ConnectionStrings["myDb"].ConnectionString;
DataContext context = new DataContext(connectionString);
Table<UserObj> usersTable = context.GetTable<UserObj>();
UserObj userObj = usersTable.SingleOrDefault(u => u.UserName == userName);
if (userObj != null)
{
string roleId = userObj.UserRoleID;
if (roleId.Equals(roleName))
return true;
}
return false;
}
Then I added [Authorize(Roles = "admin")] on the index method of a controller which I want only admin to get access. When I tried to access the page, it seems to perform a restriction ok, for example, if I entered url:
http://localhost:60353/module
..it redirected me to
http://localhost:60353/Account/LogOn?ReturnUrl=%2fmodule
However, the role didn't seem to be checked.
What have I done wrong here?
I also face same problem but i am able to call CustomProvider Method:
IsUserInRoles()
explicitly,but it does seem to be correct because the accessibility is not changed....it always redirect to login screen only.......
I've been trying for days to get OAuth working with Twitter in my Windows Phone app, but all the information I find is out dated or difficult to follow. I eventually got somewhere when I found this blog post http://samjarawan.blogspot.co.uk/2010/09/building-real-windows-phone-7-twitter_18.html which got me all the way to getting the access token, at which point it failed.
My code is almost identical to the one in the blog post, pretty much just changed the consumer key and consumer secret. Even their app doesn't work. It displays the Twitter login screen fine, and successfully authenticates, but in the RequestAccessToken function, it fails at this point:
if (String.IsNullOrEmpty(twitteruser.AccessToken) || String.IsNullOrEmpty(twitteruser.AccessTokenSecret))
{
Dispatcher.BeginInvoke(() => MessageBox.Show(response.Content));
return;
}
The really annoying thing is the message box only shows the Unicode replacement character (�) and nothing else. I also checked the response.StatusCode and it is OK, so there is no error as far as I can tell.
If someone could help me out with this, that would be great. I've seen other tutorials which require the user type in a PIN, but I haven't been able to get any of those to work either.
EDIT: I've just tried getting TweetSharp to work, but once again it fails to get the access token. Here is the code I'm using for TweetSharp:
public partial class TwitterAuthorisationPage : PhoneApplicationPage
{
private const string consumerKey = "myKey";
private const string consumerSecret = "mySecret"; // These are the correct values for my app
private const string requestTokenUri = "https://api.twitter.com/oauth/request_token";
private const string oAuthVersion = "1.0a";
private const string authorizeUri = "https://api.twitter.com/oauth/authorize";
private const string accessTokenUri = "https://api.twitter.com/oauth/access_token";
private const string callbackUri = "http://bing.com";
private TwitterService twitterService = new TwitterService(consumerKey, consumerSecret);
private OAuthRequestToken _requestToken = null;
public TwitterAuthorisationPage()
{
InitializeComponent();
}
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
twitterService.GetRequestToken((requestToken, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
_requestToken = requestToken;
Dispatcher.BeginInvoke(() => BrowserControl.Navigate(twitterService.GetAuthorizationUri(requestToken)));
}
else
{
Dispatcher.BeginInvoke(() => MessageBox.Show("Failed to connect to Twitter. Please try again.\n" + response.StatusDescription));
}
});
}
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
twitterService.GetAccessToken(_requestToken, PINEntry.Text, (accessToken, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
//These lines just print ?
System.Diagnostics.Debug.WriteLine(accessToken.Token);
System.Diagnostics.Debug.WriteLine(accessToken.TokenSecret);
twitterService.AuthenticateWith(accessToken.Token, accessToken.TokenSecret);
twitterService.VerifyCredentials((user, verifyResponse) =>
{
if (verifyResponse.StatusCode == HttpStatusCode.OK)
{
Dispatcher.BeginInvoke(() => MessageBox.Show(user.Name));
}
else
{
// Fails here
Dispatcher.BeginInvoke(() => MessageBox.Show("Failed to connect to Twitter. Please try again.1\n" + verifyResponse.StatusDescription));
}
});
}
else
{
Dispatcher.BeginInvoke(() => MessageBox.Show("Failed to connect to Twitter. Please try again.0\n" + response.StatusDescription));
}
});
}
}
EDIT 2: Could it be to do with this? https://dev.twitter.com/blog/ssl-upgrade-for-twitterapi
I worked it out! It turns out Twitter was returning the access token Gzipped. Using the method described in the blog post, I had to change the second RestClient to be constructed like so:
var client = new RestClient
{
Authority = "https://api.twitter.com/oauth",
Credentials = credentials,
HasElevatedPermissions = true,
SilverlightAcceptEncodingHeader = "gzip",
DecompressionMethods = DecompressionMethods.GZip
};
And now it works!
I am having the same problem but I didn't understand your solution, could you explain a bit more where you changed the rest client?
-----EDIT----
I finally was able to make it work with TweetSharp.
I downloaded the source code and added the lines you mentioned to the rest client configuration it uses and the compiled the project again.
Since i cannot push my changes to that github, I upload the dll here. TweetSharp recompiled dll
This is the code I use which with it works
// Step 1 - Retrieve an OAuth Request Token
Service.GetRequestToken((requestToken, response) =>
{
if (response.StatusCode == HttpStatusCode.OK)
{
Request = requestToken;
Uri uri = Service.GetAuthorizationUri(requestToken);
Dispatcher.BeginInvoke(() =>
{
Browser.Navigate(uri);
}
);
}
});
//Step 2, get the pincode
string html = Browser.SaveToString(); //gets the DOM as a string
Regex expression = new Regex(#"<code>(?<word>\w+)</code>");
Match match = expression.Match(html);
string pin = match.Groups["word"].Value;
if (pin != "")
{
loginTwitter(pin); //we login with the pin extracted
}
//step 3, get access tokens from twitter
private void loginTwitter(string pin)
{
Service.GetAccessToken(Request, pin, processAccessToken);
}
public void processAccessToken(OAuthAccessToken access, TwitterResponse Response){
if (Response.StatusCode == HttpStatusCode.OK)
{
if (access != null)
{
Access = access; // Store it for reuse
Service.AuthenticateWith(access.Token, access.TokenSecret);
}
}
}
I am developing an asp.net mvc 3.0 application which has a simple authentication process. User fills a form which is sent to server by ajax call and gets response, but the problem here is that using the following method :
FormsAuthentication.SetAuthCookie(person.LoginName,false);
is not enough to fill 'HttpContext.Current.User' and it needs the below method to be run :
FormsAuthentication.RedirectFromLoginPage("...");
Problem here is that as i mentioned, the loggin form uses an ajax form, and get responses with json, so redirecting is not possible.
How could I fill 'HttpContext.Current.User' ?
Thanks.
Update :
Here is register method :
[HttpPost]
public ActionResult Register(Person person)
{
var q = da.Persons.Where(x => x.LoginName == person.LoginName.ToLower()).FirstOrDefault();
if (q != null)
{
ModelState.AddModelError("", "Username is repettive, try other one");
return Json(new object[] { false, this.RenderPartialViewToString("RegisterControl", person) });
}
else
{
if (person.LoginName.ToLower() == "admin")
{
person.IsAdmin = true;
person.IsActive = true;
}
da.Persons.Add(person);
da.SaveChanges();
FormsAuthentication.SetAuthCookie(person.LoginName,false);
return Json(new object[] { true, "You have registered successfully!" });
}
}
FormsAuthentication doesn't support immediate setting of user's identity, but you should be able to fake it by something like this:
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(person.LoginName),
new string[] { /* fill roles if any */ } );
Here is the version I ended up using, which is based on the answer by #AdamTuliper-MSFT. It is only meant to be used right after logging in, but before redirect, to allow other code to access HttpContext.User.
Don't do anything if already authenticated
Doesn't modify the cookie, since this should only be used for the lifetime of this request
Shorten some things, and a little safer with userdata (should never be null, but...)
Call this after you call SetAuthCookie(), like below:
// in login function
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
AuthenticateThisRequest();
private void AuthenticateThisRequest()
{
//NOTE: if the user is already logged in (e.g. under a different user account)
// then this will NOT reset the identity information. Be aware of this if
// you allow already-logged in users to "re-login" as different accounts
// without first logging out.
if (HttpContext.User.Identity.IsAuthenticated) return;
var name = FormsAuthentication.FormsCookieName;
var cookie = Response.Cookies[name];
if (cookie != null)
{
var ticket = FormsAuthentication.Decrypt(cookie.Value);
if (ticket != null && !ticket.Expired)
{
string[] roles = (ticket.UserData as string ?? "").Split(',');
HttpContext.User = new GenericPrincipal(new FormsIdentity(ticket), roles);
}
}
}
Edit: Remove call to Request.Cookies, as #AdamTuplier-MSFT mentioned.
You need to manually set it. Rather than reinventing the wheel, note the section here on updating the current principal for the request - thats your option here.
How to set Request.IsAuthenticated to true when not using FormsAuthentication.RedirectFromLoginPage?
public void RenewCurrentUser()
{
System.Web.HttpCookie authCookie =
System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = null;
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null && !authTicket.Expired)
{
FormsAuthenticationTicket newAuthTicket = authTicket;
if (FormsAuthentication.SlidingExpiration)
{
newAuthTicket = FormsAuthentication.RenewTicketIfOld(authTicket);
}
string userData = newAuthTicket.UserData;
string[] roles = userData.Split(',');
System.Web.HttpContext.Current.User =
new System.Security.Principal.GenericPrincipal(new FormsIdentity(newAuthTicket), roles);
}
}
}