Client Credentials Grant on CRM Dynamics 2016 On Premise Web API - asp.net-web-api

I am trying to call the CRM Dynamics On Premise 2016 Web API.
I configured Authorization Code Flow using OAuth and it is working. But, i need to set up the Client Credentials flow since many applications are running on background and they can't be prompted with login screen.
Since, its On Premise, we dont have Azure AD.
Where do we go and register our application?
Is there another way to access Web API for On premise dynamics CRM( For example userid,password etc)

Xrm api is accessible via client credentials without any special setup (be it on-prem or on-line) - you just setup S2S user with appropriate permissions, and you can log him in like:
static void InContext(Action<IOrganizationService> callback, Org org)
{
var credentials = new ClientCredentials();
if (!org.IsLocal)
{
credentials.UserName.UserName = org.UserName;
credentials.UserName.Password = org.Password;
}
else
{
credentials.Windows.ClientCredential = new NetworkCredential(org.UserName, org.Password);
}
using (var serviceProxy =
new OrganizationServiceProxy(new Uri(org.OrganizationServiceUri),
null, credentials
, null))
{
callback.Invoke(serviceProxy);
}
}
public class Org
{
public string UserName { get; set; }
public string Password { get; set; }
public string OrganizationServiceUri { get; set; }
public bool IsLocal { get; set; }
public Org()
{
}
public Org(bool isLocal, string userName, string password, string organizationServiceUri)
{
IsLocal = isLocal;
UserName = userName;
Password = password;
OrganizationServiceUri = organizationServiceUri;
DiscoveryServiceUri = discoveryServiceUri;
}
}
And then in your backend code:
var org = new Org(true, "Administrator", "Password",
"http://ondracrm/org/XRMServices/2011/Organization.svc");
InContext((os) => {
// some sample work with organization service
var response = (RetrieveEntityResponse)os.Execute(
new RetrieveEntityRequest
{
LogicalName = "contact",
EntityFilters = EntityFilters.Attributes
});
}, org);

Related

Blazor Session Storage

At my current project(blazor server side) I want to start using the session storage for user data like roles and names.
I've tried Blazored.SessionStorage and AspNetCore.Components.Server.ProtectedBrowserStorage.
The problem I'm facing is, that I just can't get the value(it's always null) and I don't know why.
Code I'm using:
public void GetUserInfo()
{
var x = sessionStorage.GetAsync<string>("Name");
var y = sessionStorage.GetAsync<string>("Email");
string Name = x.ToString();
string Email = y.ToString();
}
And
[Inject] public ProtectedSessionStorage sessionStorage { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
string Name = Helper.Name;
string Email = Helper.Email;
await sessionStorage.SetAsync("Name", Name);
await sessionStorage.SetAsync("Email", Email);
var x = sessionStorage.GetAsync<string>("Name");
var y = sessionStorage.GetAsync<string>("Email");
Name = x.Result.Value;
Email = y.Result.Value;
}
Thanks to everyone in advance and have a great day! :)
DO NOT USE THIS SOLUTION AS IS. WHEN I GET THE TIME I WILL UPDATE IT TO A WORKING SOLUTION
I suggest adding this as an injected object using Dependency Injection.
Create a class to hold this information and add is as a Scoped service.
Class:
public class UserInfo : IUserInfo //Create an interface
{
public static Name { get; set; }
public static Email { get; set; }
}
Injection (Program.cs on .NET 6):
public static async Task Main(string[] args)
{
//For WSAM
var builder = WebAssemblyHostBuilder.CreateDefault(args);
//For Server
var builder = WebApplication.CreateBuilder(args);
...
builder.Services.AddScoped<IUserInfo, UserInfo>(); //Scoped Service injection
}
Add data to injected service:
[Inject]
public IUserInfo UserInfo { get; set; }
protected override void OnInitialized() //Use whatever Life Cycle methods works for your implementation
{
UserInfo.Name = Helper.Name;
UserInfo.Email = Helper.Email;
}
Usage example:
#inject IUserInfo UserInfo
#page "/"
<div>#UserInfo.Name</div>
<div>#UserInfo.Email</div>

Automatonymous - Payload not found when calling RaiseEvent with Send Activity

I've been spinning my wheels trying to get the MassTransitStateMachine working and I seem to not understand exactly how this is supposed to work.
The error I'm receiving (full code below) is PayloadNotFoundException when I attempt to call stateMachine.RaiseEvent(instance, stateMachine.DeployApplicationRequest, request) combined with the .Send() Activity in the state machine. I've been trying to trace this through but have gotten nowhere.
Here's the relevant code and explanation of intent:
I receive a request to deploy an application via a webAPI post, and convert the request to a DeployApplicationRequest.
public record DeployApplicationRequest
{
// State machine properties
public Guid CorrelationId { get; init; }
public ChatApplication ChatApplication { get; init; }
public string ChannelId { get; init; }
public string UserId { get; init; }
// Command
public DeployApplication DeployApplication { get; init; }
}
public enum ChatApplication
{
Slack,
Teams
}
This request encapsulates two things: The state that the API needs to maintain so it knows how to route the results back to the requester (state machine properties) and the Command to be sent to the service bus.
The command, DeployApplication, looks like this:
public record DeployApplication
{
public Guid CorrelationId { get; init;}
public string InitiatedBy { get; init; }
public DateTime CreatedDate { get; init; }
public string Environment { get; init; }
public string PullRequestId { get; init; }
}
I have created a state instance to preserve the details of the request (such as whether it came in via Slack or Teams). I do not want to publish this information to the bus:
public class DeployApplicationState : SagaStateMachineInstance
{
public Guid CorrelationId { get; set; }
public string CurrentState { get; set; }
public ChatApplication ChatApplication { get; set; }
public string ChannelId { get; set; }
public string UserId { get; set; }
}
And I have created a StateMachine to handle this:
public class DeployApplicationStateMachine : MassTransitStateMachine<DeployApplicationState>
{
private readonly ILogger<DeployApplicationStateMachine> logger;
public DeployApplicationStateMachine(ILogger<DeployApplicationStateMachine> logger)
{
this.logger = logger;
InstanceState(x => x.CurrentState);
Event(() => DeployApplicationRequest, x => x.CorrelateById(context => context.Message.CorrelationId));
Initially(
When(DeployApplicationRequest)
.Then(x => {
x.Instance.CorrelationId = x.Data.CorrelationId;
x.Instance.ChannelId = x.Data.ChannelId;
x.Instance.ChatApplication = x.Data.ChatApplication;
x.Instance.UserId = x.Data.UserId;
})
.Send(context => context.Init<DeployApplication>(context.Data.DeployApplication))
.TransitionTo(Submitted));
}
public Event<DeployApplicationRequest> DeployApplicationRequest { get; private set; }
public State Submitted { get; private set; }
}
To trigger the initial event (since the request is not coming in via a Consumer but rather through a controller), I have injected the state machine into the MassTransit client, and I'm calling the RaiseEvent method:
public class MassTransitDeployClient : IDeployClient
{
private readonly DeployApplicationStateMachine stateMachine;
public MassTransitDeployClient(DeployApplicationStateMachine stateMachine)
{
this.stateMachine = stateMachine;
}
public async Task Send(DeployApplicationRequest request)
{
var instance = new DeployApplicationState
{
CorrelationId = request.CorrelationId,
ChannelId = request.ChannelId,
ChatApplication = request.ChatApplication,
UserId = request.UserId
};
await stateMachine.RaiseEvent(instance, stateMachine.DeployApplicationRequest, request);
// This works for sending to the bus, but I lose the state information
//await sendEndpointProvider.Send(request.DeployApplication);
}
}
And the container configuration is as follows:
services.AddMassTransit(x =>
{
x.AddSagaStateMachine<DeployApplicationStateMachine, DeployApplicationState>()
.InMemoryRepository();
x.UsingInMemory((context, cfg) =>
{
cfg.ConfigureEndpoints(context);
});
});
EndpointConvention.Map<DeployApplication>(new Uri($"queue:{typeof(DeployApplication).FullName}"));
services.AddMassTransitHostedService();
Raising the event works just fine, and the state machine transfers to Submitted successfully if I do not include .Send(...) in the state machine. The second I introduce that Activity I get the PayloadNotFoundException. I've tried about 15 different ways of doing this over the weekend with no success, and I'm hoping someone may be able to help me see the error of my ways.
Thanks for reading! Fantastic library, by the way. I'll be looking for ways to contribute to this going forward, this is one of the most useful libraries I've come across (and Chris your Youtube videos are excellent).
Saga state machines are not meant to be invoked directly from controllers. You should send (or publish) a message that will then be dispatched to the saga via the message broker.
IF you want to do it that way, you can use MassTransit Mediator instead, but you'll still be sending a message via Mediator to the saga, which will then be handled by MassTransit.
TL;DR - you don't use RaiseEvent with saga state machines.

How to get IOptions in ConfigureServices method or pass IOptions into extension method?

I'm developing asp .net core web api 2.1 app.
I add JWT authentication service as an extension method in static class:
public static class AuthenticationMiddleware
{
public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, string issuer, string key)
{
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
// validate the server that created that token
ValidateIssuer = true,
// ensure that the recipient of the token is authorized to receive it
ValidateAudience = true,
// check that the token is not expired and that the signing key of the issuer is valid
ValidateLifetime = true,
// verify that the key used to sign the incoming token is part of a list of trusted keys
ValidateIssuerSigningKey = true,
ValidIssuer = issuer,
ValidAudience = issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key))
};
});
return services;
}
}
which I use in ConfigureServices method of Startup class like this:
public void ConfigureServices(IServiceCollection services)
{
// adding some services omitted here
services.AddJwtAuthentication(Configuration["Jwt:Issuer"], Configuration["Jwt:Key"]);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Now, I have a requirement to use IOptions pattern to get JWT authentication data from appsettings.json
How can I get IOptions in ConfigureServices method to pass issuer and key into extension method? Or how to pass IOptions to extension method?
For binding data from appsettings.json to Model, you could follow steps below:
Appsettings.json content
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"JWT": {
"Issuer": "I",
"Key": "K"
}
}
JWT Options
public class JwtOptions
{
public string Issuer { get; set; }
public string Key { get; set; }
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<JwtOptions>(Configuration.GetSection("JWT"));
var serviceProvider = services.BuildServiceProvider();
var opt = serviceProvider.GetRequiredService<IOptions<JwtOptions>>().Value;
services.AddJwtAuthentication(opt.Issuer, opt.Key);
services.AddMvc();
}
One more option to pass JwtOptions directly.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<JwtOptions>(Configuration.GetSection("JWT"));
var serviceProvider = services.BuildServiceProvider();
var opt = serviceProvider.GetRequiredService<IOptions<JwtOptions>>().Value;
services.AddJwtAuthentication(opt);
services.AddMvc();
}
Change the extension method.
public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, JwtOptions opt)
One other option is to bind the configurations to a class with the Bind() extension. (IMO this a more clean solution then the IOptions)
public class JwtKeys
{
public string Issuer { get; set; }
public string Key { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
var jwtKeys = new JwtKeys();
Configuration.GetSection("JWT").Bind(JwtKeys);
services.AddJwtAuthentication(jwtKeys);
}
public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, JwtKeys jwtKeys)
{....}
Then if you need the JwtKeys settings some other place in the solution, just register the class on the collection and inject it where needed
services.AddSingleton(jwtKeys);
You can add your options to DI container in Startup class like this:
public class JwtOptions
{
public string Issuer { get; set; }
public string Key { get; set; }
}
public void ConfigureService(IServiceCollection services)
{
services.AddOptions();
services.Configure<JwtOptions>(Configuration.GetSection("Jwt"));
}
Now you can use this options, in a configure stage, or in an extension method:
public void Configure(IApplicationBuilder app)
{
var options = app.ApplicationServices.GetService<IOptions<JwtOptions>();
// write your own code
}

Implement OAuth with ASP.NET WebAPI

Hello Friends I would like to ask you that can any one have an example to integrate oAuth in ASP.Net web api with out integrating any packages or Entity framework??i search it a lot but find a various way using nuget packages and other packages but i need the way using simple third party calls because i need this authorization in .net as well as java api's. Can any one help me out in this.
Thanks in advance...
Yes you can do this, i implemented this in my web api using oAuth in web api 2 project.
First, have an asp.net project with oauth is configured since we will take cooy some files into web api project.
Here is the steps:
1) In the web api, add a new class file called "IdentityConfig.cs".
This class will have: ApplicationUser, ApplicationUserManager, ApplicationSignInManager and ApplicationDbContext classes.
2) Make sure that these classes above is under your api namespace so it is accessible through all your controllers.
// Configure the application user manager which is used in this api.
public class ApplicationUser : IdentityUser
{
#region custom properties
public string Name { get; set; }
public int? ZipCode { get; set; }
public long? CountryId { get; set; }
public bool IsDeleted { get; set; }
public bool EmailConfirmed { get; set; }
public DateTime CreatedDate { get; set; }
public long UserId { get; set; }
#endregion
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = true,
RequireUppercase = false,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
//manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this api.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DBCONNECTIONKEY", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Note: DBCONNECTIONKEY is the key for the connection string in web.config
3) Add Startup.cs file to the root of your web api. copy the logic from the existing one you have in asp.net. feel free to tweak the configuration context properties as needed in the web api project.
4) Use objects from these classes to sign in users, and manager application user objects as you have in asp.net web app.
That's all :)
Hope this helps.

How to create SESSION in Windows Phone

string uname = txt1.Text;
string pwd = txt2.Text;
NavigationService.Navigate(new Uri("/newPage.xaml?name="+uname+"&pwd="+pwd,UriKind.Relative));
I have two text boxes: username and password
now I am entering values on those textboxes and those values are for example:
username: abcd
password:1234
now I want those values in multiple pages, so how it will possible?
I am using query string but every time I have to define values with navigation URI,
So please suggest me any other way like SESSION in ASP.NET.
public class Users
{
public string Username { get; set; }
public string Password { get; set; }
}
Users objUser = new Users();
objUser.Username = "Viraj";
objUser.Password = "12345";
//save data in phone state use in multiple pages.
PhoneApplicationService.Current.State["UserInfo"] = objUser;
//To retrieve data on another screen from phone state
if(PhoneApplicationService.Current.State["UserInfo"]!=null)
{
Users objUser = PhoneApplicationService.Current.State["UserInfo"] as Users;
}
//To update data in phone state
if(PhoneApplicationService.Current.State["UserInfo"]!=null)
{
Users objUser = PhoneApplicationService.Current.State["UserInfo"] as Users;
objUser.Username = "aman";
PhoneApplicationService.Current.State["UserInfo"] = objUser;
}
//at last remember that always remove data from phone state on app exist
private void Application_Closing(object sender, ClosingEventArgs e)
{
if(PhoneApplicationService.Current.State["UserInfo"]!=null)
{
PhoneApplicationService.Current.State.Remove("UserInfo");
}
}
Once the login is success, you can copy the Username and Password to static variables which have same namespace as of the app so that it will be accessible in every pages.
public static string Username;
public static string Password;
Hope this will solve your issue
Create a static public variable you set. Example:
public static class AppState
{
public static string Username { get; set; }
public static string Password { get; set; }
}
Then you can simple set the values any where:
AppState.Username = "Viraj";
IsolatedStorageSettings.ApplicationSettings["uname"] = uname;
then call it in other page like:
string name = IsolatedStorageSettings.ApplicationSettings["uname"] as string;
this also works greater.

Resources