Add data to Identity default database using connectionstring - c#

I am currently working on a learning project where I used to use forms auth with an sql database. But today I have updated to using Indetity.
The problem I have now is that my site stores more than user information, and I want that in the same database as the Identity data.
When I used membership this was no problem, I just added a connectionstring and wrote an SQL statement. But now it seems I need to add something called DbContext?
Maybe it's easier to look at my code to understand, This is my old code, used for old SQL database:
using (SqlConnection con = new SqlConnection(strCon))
{
using (SqlCommand cmd = con.CreateCommand())
{
cmd.CommandText = "INSERT INTO Website (url, rating, categoryId, subCategoryId, description1, description2) values (#url, #rating, #categoryId, #subCategoryId, #desc1, #desc2)";
cmd.Parameters.AddWithValue("#url", url);
cmd.Parameters.AddWithValue("#rating", rating);
cmd.Parameters.AddWithValue("#categoryId", categoryId);
cmd.Parameters.AddWithValue("#desc1", desc1);
cmd.Parameters.AddWithValue("#desc2", desc2);
if (DPLSubCategory.Items != null)
{
Int32 subCategoryId = Convert.ToInt32(DPLSubCategory.SelectedItem.Value);
cmd.Parameters.AddWithValue("#subCategoryId", subCategoryId);
}
con.Open();
cmd.ExecuteNonQuery();
}
con.Close();
}
Now say I add a "Website" table to my local Identity database. How can I do something similar to the code above? Right now I am using default connectionstring:
<add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\WDBAPP.mdf;Initial Catalog=WDBAPP;Integrated Security=True" providerName="System.Data.SqlClient"/>
And I add members like this:
// Default UserStore constructor uses the default connection string named: DefaultConnection
var userStore = new UserStore<IdentityUser>();
var manager = new UserManager<IdentityUser>(userStore);
var user = new IdentityUser() { UserName = UserName.Text };
IdentityResult result = manager.Create(user, Password.Text);
if (result.Succeeded)
{
var authenticationManager = HttpContext.Current.GetOwinContext().Authentication;
var userIdentity = manager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties() { }, userIdentity);
Response.Redirect("~/Login.aspx");
}
As you can see I have no need to point to my database since I use the default connectionstring.
So how do I change my connectionstring to, for example, "MyConString" and still have it work with the registration code? I want to be able to point to my database from my code so that I can add whatever I want.

Web forms do not have a applicationdbcontext class that I can find
and modify, and I do not know how to create one.
Default Web Form Template in VS 2013 has ApplicationDbContext.
If you are new to ASP.Net Identity, you want to use Default Template created by VS first before hand-rolling your own UserManager, because a lot of things can go wrong easily.
If you want to keep ASP.Net Identity inside your existing database, you need to rename it same as your existing connection string.

Related

Convert Entity Framework DB Connection to use Azure Identity using Powershell Credentials

We have an application that is setup to use EntityFramework. I need to update that application to access the database using Azure Identity. Basically I need to do the following:
Get the access token using AzurePowershell Credentials
Create the SQL Connection using that access token
Add the token to the connection and return it to the caller
Here's what I have so far (but I know i'm missing alot of things here coz I am getting compilation errors). I was looking for some examples to do this but wasn't successful.
public static DbConnection CreateConnection( string efConnectionString )
{
var credential = new AzurePowerShellCredential();
System.Threading.CancellationToken cancellationToken = default;
TokenRequestContext requestContext = "https://database.windows.net/.default";
string accessToken = AzurePowerShellCredential.GetToken( "https://database.windows.net/.default", cancellationToken );
SqlConnectionStringBuilder sqlConnection = new SqlConnectionStringBuilder( efConnectionString );
//create sql connection
using( SqlConnection sqlConn = new SqlConnection( efConnectionString ) )
{
sqlConn.AccessToken = accessToken;
return sqlConn;
}
For starters the requestContext doesn't like being set up as a string and I'm trying to figure out the best way to add the token to the connection string and send it back successfully. Also, the AzurePowerShellCredential.GetToken doesn't like the string that I am passing with the database address.
My Connection string that is coming in looks like this:
"Server=tcp:servername.database.windows.net,1433;Database=databasename;User ID=UserID#servername;Password=password;Trusted_Connection=False;Encrypt=True;"
In order for this to work successfully, I will be ommitting the userid and password from the above connections string so it can be replaced by the AzureCredentials.
I really appreciate any help that can be provided. Thanks!
Your code is correct. The only thing I would suggest is to use DefaultAzureCredential which would allow you to use different authentication flows for the database.
As for a connection string it's format can be following:
using Azure.Core;
using Azure.Identity;
using System.Data.SqlClient;
var connString = "Server=tcp:<your-server-name>.database.windows.net,1433;Database=<database-name>;";
var credential = new DefaultAzureCredential();
var token = credential
.GetToken(
new TokenRequestContext(scopes: new[] { "https://database.windows.net/.default" })
);
using var conn = new SqlConnection(connString);
conn.AccessToken = token.Token;
conn.Open();
One more thing you should think about is access token renewal. EntityFramework caches database connections so the token might expire while the connection is alive. As an option I can suggest you to use existing solution like in this example if you have an ASP.NET app or try to reverse-engineer token update logic like in Microsoft.Data.SqlClient (example)

DBMigrator fails to read accesstoken in ef6 codefirst - SQL Connection

I have a WebAPI with ef6 code first setup and working. In app start we have dbmigrator.Update() which applies any pending migrations to database.
After changing the connection string to remove username and password and provide access token instead, dbmigrator.Update() fails with an error:
Login failed for user ''
How to ensure that dbmigrator works with Azure SQL access token instead of username/password in connection string?
Edit 1:
The change done to dbcontext constructor is to change it from
DbContext() : base("nameofConnString"){}
to
DbContext() : base(GetSQLConn(), true)
{
Database.SetInitializer<DbContext>(null);
}
With GetSQLConn(), I am retrieving a connection without uname/pwd and attaching accesstoken to it and returning the connection!
Edit 2:
private static SqlConnection GetSQLConn()
{
var accessToken = TokenFactory.AcquireToken();
var connString = ConfigurationManager.ConnectionStrings["SQLConnectionString"].ConnectionString;
var conn = new SqlConnection(connString)
{
AccessToken = accessToken,
};
return conn;
}
How to ensure that dbmigrator works with Azure SQL access token instead of username/password in connection string?
actually dbcontext works for CRUD operations on all my tables, just this migrator won't work!
According to your comment, it seems that you have no permission to alert table. If you don't grant corresponding permission for the created user. Please have a try to grant permission to the created user. More details about db role please refer to the Database-Level Roles.
EXEC sp_addrolemember N'db_owner', N'your-user-name'
I also following the SO thread you mentioned.If I add the user to do_owner, I test it with Alter Table, it works correctly on my side. The following is my detail steps.
1.Registry an Azure AD Application
2.Provision an Azure Active Directory administrator for your Azure SQL server, more details please refer to the this tutorials
3.Create user for the Azure SQL and grant corresponding permission.
CREATE USER [RegistryAppName] FROM EXTERNAL PROVIDER
EXEC sp_addrolemember N'db_owner', 'RegistryAppName'
4.Change the demo code and run it as expected.
SqlConnectionStringBuilder builder =
new SqlConnectionStringBuilder
{
["Data Source"] = "azureServername.database.windows.net",
["Initial Catalog"] = "databaseName",
["Connect Timeout"] = 30
};
// replace with your server name
// replace with your database name
string accessToken = TokenFactory.GetAccessToken();
if (accessToken == null)
{
Console.WriteLine("Fail to acuire the token to the database.");
}
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
try
{
connection.AccessToken = accessToken;
connection.Open();
var commentText = "ALTER TABLE AccountRec ADD newColumn varchar(10) ";
SqlCommand sqlCommand = new SqlCommand(commentText, connection);
Console.WriteLine("Executed Result:" + sqlCommand.ExecuteNonQuery());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Console.WriteLine("Please press any key to stop");
Console.ReadKey();

C# MVC getting database permissions from user login

I'm wondering what the proper way is to handle SQL permissions and store connection strings is in a C# MVC application.
Up until now I have done work around Windows authentication, so it has been set as such in the web.config connection string to use windows authentication and all database calling and writing has been done via the database context referencing that.
<add name="ConnectionStringName" connectionString="data source=SqlDB; initial catalog=DBNAME;persist security info=True; Integrated Security=SSPI;" providerName="System.Data.SqlClient"/>
public class DatabaseContext : DbContext
{
public DatabaseContext() : base("name = ConnectionStringName")
{
}
... ...
controller code sample
public class MyController : Controller
{
public DatabaseContext db = new DatabaseContext();
// GET:
public ActionResult myView()
{
NewMmodel model = new NewModel();
model.table = db.table.ToList();
return View(model);
}
[HttpPost]
public ActionResult Add([Bind(Include ="MyModel")] Add model)
{
db.Database.ExecuteSqlCommand("EXEC dbo.usp....
I now need to make something that people login with a username and password and will pull permissions from there.
My first thought would have been to make the web.config connection string "Dynamic" but it doesn't seem that this is possible? so probably not the proper way of doing it even if it is then?
Is it same to store a persisted username and password in a main connection string to allow initial reading from a database? if so is it then best to create a connection string in a class?
so in a data access layer class have something like:
public ProcessContext() : base(ConnectionString())
{
}
private static string ConnectionString()
{
SqlConnectionStringBuilder sqlBuilder = new SqlConnectionStringBuilder();
sqlBuilder.DataSource = "XXX";
sqlBuilder.InitialCatalog = "YYY";
sqlBuilder.PersistSecurityInfo = true;
sqlBuilder.IntegratedSecurity = false;
sqlBuilder.MultipleActiveResultSets = true;
sqlBuilder.UserID = "xxx";
sqlBuilder.Password = "yyy";
return sqlBuilder.ToString();
}
I'm getting an error with this though saying ProcessContext() Must have a return type
If I am on the right track, finally what I want to be able to do is replace the USERID and password values above with the ones the user has logged in with.
I'm using the pre-built code visual studio pulls in with mvc 5 for the manual login.
I believe it comes from the SetupAuth.cs file and users the tables like AspNetUsers, roles Logins & Claims that it creates itself.
I can't really see where you can access those session values, I just need to add the [Authorize] tag above a call in the controller to check for a correct login.
Hope this all makes sense! Is this Owin/OAuth? everything i try to look at regarding OAuth talks about google or facebook login...

Custom Role Authentication ASP .NET 4.5

I am really struggling with creating my own user access roles and utilising the IIdentity and IPrincipal classes.
EDIT
I have been looking at this but I can't get it to work.
I have created a simple EndUser table with email, password and roles columns. However at the moment, I don't think the roles isn't being parsed through to the IIdentity or IPrincipal and I am unsure of how to do it.
I have looked at several guides and most either rely on VS and the .NET Framework building the basis for you with many clunky looking tables, or they are MVC, which isn't helpful at all.
In my table I have three defined roles which are Client, Sales and Admin, I want certain pages inaccessible to certain users. These are then reflected in my webconfig as Denying access globally but allowing access to certain roles and only if authenticated.
At present the login functionality is working, and users are denied unless logged in but the specific roles are not being taken into account. I have an object stored in session of all of the user data across the site, but "roles" is completely ignored in the web config.
Ultimately I need to do something with uRole, but I am not sure what or how.
Could someone point me in the right direction please?
Many thanks
Login C#:
public partial class Login : Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void logindd_Click(object sender, EventArgs e)
{
EndUser newEndUser = new EndUser();
string user = ((TextBox)loginForm1.FindControl("UserName")).Text;
string conRef = ((TextBox)loginForm1.FindControl("Password")).Text;
using (SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["LocalConnection"].ConnectionString))
{
try
{
con.Open();
string checkIdent = #"SELECT Count(*) FROM EndUser WHERE EndUser.Email=#email AND EndUser.Password=#pass";
using (SqlCommand cmd = new SqlCommand(checkIdent, con))
{
cmd.Parameters.AddWithValue("#email", user);
cmd.Parameters.AddWithValue("#pass", pass);
int chk = (int)cmd.ExecuteScalar();
if (chk > 0)
{
//grabbing user role
SqlCommand cmd2 = new SqlCommand("SELECT EndUser.Role FROM EndUser WHERE EndUser.Password=#pass;",con);
cmd2.Parameters.AddWithValue("#pass", pass);
SqlDataReader sdr = null;
sdr = cmd2.ExecuteReader();
sdr.Read();
string uRole = sdr["Role"].ToString();
UserIdentity userr = new UserIdentity(user, true, uRole);
userr.Roles.Add(uRole);
FormsAuthentication.Initialize();
FormsAuthenticationTicket fat = new FormsAuthenticationTicket(1, user, DateTime.Now, DateTime.Now.AddMinutes(30), false, userr.Roles.ToString());
Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
newEndUser.email = user;
newEndUser.pass = pass;
Session["EndUserObj"] = newEndUser;
FormsAuthentication.RedirectFromLoginPage(user, false);
}
else
{
ErrorLbl.Visible = true;
ErrorLbl.Text = "Details are incorrect";
}
}
}
catch (Exception er)
{
ErrorLbl.Visible = true;
ErrorLbl.Text = er.ToString();
}
}
}
}
I had this issue when I was trying to sort this out a few months back. You're currently logging in at the moment, from the code that you've shown I would have to assume that this is a web forms project with pre-built Web Form kit. Therefore you're still using OWIN to login.
Not a code-based walk-through but this is what you need to do, I'm happy to assist further if you get stuck:
Change the global.asax so that it authenticates the user on Application_AthenticateRequest and remove anything related to OWIN in it's methods.
You need a class to derive from IIdentity and accept params of username, authtype and isAuthenticated and a class to derive from IPrincipal which will contain the derived IIdentity, and the role. I went for public getter and setters and private variables of things like name, is auth etc.
A class to get the identity of the user, this where you'll get against the password and username and create an instance of the derived IIdentity to set, also check against the information held within the database.
The global method Application_AthenticateRequest will need to verify the user on each page load, so you need to have a method that creates an instance of the derived IPrincipal to check whether the user is authenticated by grabbing the derived IIdentity and checking what role the user is in.
Modify the webconfig so that it uses FormsAuthentication, doesn't use OWIN, so within AppSettings add <add key="owin:AutomaticAppStartup" value="false"/> as I assume you have a pre-built site, don't try and remove OWIN, just disable it.
You should then be able to login and the custom roles you declare will then be used by the derived IIdentity which in turn will be acknowledged by the webconfig location based security.
Hope this helps you and others.

Migrating plain-text password to asp.net Identity 2.1

I am moving an existing site to mvc and one of the thing that puzzles me is the migration of user data, particularly passwords, these passwords are stored as plain text (yes i know). Now moving the rest of the account information to aspnet identity tables is easy but I need to now hash these passwords and I am not sure how to properly hash them before storage (using tsql), if i must use a .net app to hash, what class do i need to call by feeding it plain password and it will give me a hash?
thank you
It seems like your application isn't set up right for a complete migration, so I have an idea for an easy solution to get the full use out of the Identity 2.0 database and features.
Create New Web Application, then create a method that gets the plain text login credientials for your old users. Next:
When you create a new application look at the code in the account/register files.
Here is what you see:
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = Context.GetOwinContext().Get<ApplicationSignInManager>();
var user = new ApplicationUser() { UserName = Email.Text, Email = Email.Text };
IdentityResult result = manager.Create(user, Password.Text);
if (result.Succeeded)
{
}
Now create a class to hold all the properties from your existing user, or you could use a SQLDataReader["columnName"] if you wanted to go the rout of looping a reader.
So copy that code run your new Method:
var ListOfUsers = MyMethodThatRetunsAllOldMembers();
foreach member in ListOfUsers{
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signInManager = Context.GetOwinContext().Get<ApplicationSignInManager>();
var user = new ApplicationUser() { UserName = Member.UserName, Email = Member.Email };
IdentityResult result = manager.Create(member.LoginName, Member.Password);
//Example: You can even add roles at the same time.
var ra = new RoleActions();
ra.AddUserToRoll(member.LoginName, Member.Email, userRole);
}
This will create the new hashed passwords as well as settings roles and userid's for each of your old users, effectively migrating your database.

Categories

Resources