How to user previous resultset into next query in SQL Server - sql-server

I want to generate access token if username and password will match in users table. But how can I use previous returned value to next insert query? I have marked missing value with ###I don't know###.
CREATE PROCEDURE [dbo].[GetAccessToken]
#userId varchar(50),
#password varchar(50)
AS
SELECT Id
FROM Users
WHERE UserId = #userId AND Password = #password
IF ##ROWCOUNT != 0
BEGIN
SET #token = NEWID();
INSERT INTO AccessTokens (Token, UserId)
VALUES(CONVERT(varchar(255), #token), ###I Don't Know###)
END
RETURN 0
END
Please help me. Thanks, in advance.

Simply pass the #userId as your second parameter for INSERT
INSERT INTO AccessTokens (Token, UserId)
VALUES(CONVERT(varchar(255), #token), #userId)

Try this:
CREATE PROCEDURE [dbo].[GetAccessToken]
#userId varchar(50),
#password varchar(50)
AS
DECLARE #ID INT
SELECT #ID = Id
FROM Users
WHERE UserId = #userId AND Password = #password
IF (#ID IS NOT NULL )
BEGIN
SET #token = NEWID();
INSERT INTO AccessTokens (Token, UserId)
VALUES(CONVERT(varchar(255), #token), #ID)
RETURN 0
END

Use option with INSERT .. SELECT .. pattern
CREATE PROCEDURE [dbo].[GetAccessToken]
#userId varchar(50),
#password varchar(50)
AS
BEGIN
INSERT INTO AccessTokens (Token, UserId)
SELECT CONVERT(varchar(255), NEWID()), Id
FROM Users
WHERE UserId = #userId AND Password = #password
END

Related

Creating procedure to login with password hashed and salt, returning Incorrect password

Ok, so I am making an ASP.net website application that has a registration form that will take in details about the user and the password will be encrypted with a registration procedure that will hash and salt the password. Now the problem is that when creating a login procedure. The login procedure that I implemented is displaying "Incorrect password" on #responseMessage output when passing in the username and password. What could be the problem and how to fix this issue?
RestuarantProc - Registration Procedure
CREATE PROCEDURE [dbo].[RestuarantProc]
#FirstName NVARCHAR(50),
#LastName NVARCHAR(50),
#UserName NVARCHAR(50),
#Password NVARCHAR(50),
#Age INT,
#Email VARCHAR(100),
#responseMessage NVARCHAR(250) OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE #salt UNIQUEIDENTIFIER=NEWID()
BEGIN TRY
INSERT INTO RestuarantReg(FirstName, LastName, UserName, PasswordHash, PasswordSalt, Age, Email)
VALUES(#FirstName, #LastName, #UserName, HASHBYTES('SHA2_512', #Password+CAST(#salt AS NVARCHAR(36))),
#salt, #Age, #Email)
SET #responseMessage='Success'
END TRY
BEGIN CATCH
SET #responseMessage=ERROR_MESSAGE()
END CATCH
END
GO
DECLARE #responseMessage NVARCHAR(250)
EXEC dbo.RestuarantProc #FirstName = 'Bob', #LastName = 'Hudgins', #UserName = 'bobh123', #Password = 'password',
#Age = '23', #Email = 'bob#bob.com', #responseMessage = #responseMessage OUTPUT
SELECT * FROM RestuarantReg
RestuarantProcLogin - Login Procedure
CREATE PROCEDURE RestuarantProcLogin
#userName NVARCHAR(254),
#password NVARCHAR(50),
#responseMessage NVARCHAR(250)='' OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE #userID INT
IF EXISTS (SELECT TOP 1 Id FROM [dbo].[RestuarantReg] WHERE UserName=#userName)
BEGIN
SET #userID=(SELECT Id FROM [dbo].[RestuarantReg]
WHERE UserName=#userName AND
PasswordHash=HASHBYTES('SHA2_512', #password+CAST(PasswordSalt AS NVARCHAR(36))))
IF(#userID IS NULL)
SET #responseMessage='Incorrect password'
ELSE
SET #responseMessage='User successfully logged in'
END
ELSE
SET #responseMessage='Invalid login'
END
DECLARE #responseMessage nvarchar(250)
EXEC dbo.RestuarantProcLogin #userName = N'bobh123', #password = N'password', #responseMessage = #responseMessage OUTPUT
SELECT #responseMessage as N'#responseMessage'
Move the calculation of the hash into a function. Easy to do, easy to maintain, single point of definition. Problem solved if you keep the datastore for your vars the same as your table.

Check transaction committed and if so return a value

I want to insert a new user (Email address) into a table of users. If successful (no duplicate), insert basic configurations for new user to other tables, and return some data to client. If not successful, receive relevant notification in dataset in C# project.
Here is sample code so you can easily comment on it
ALTER PROCEDURE [dbo].[CreateUser]
#Email nvarchar(256),
#Password nvarchar(256)
AS
BEGIN
declare #UserID uniqueidentifier;
declare #ConfigID uniqueidentifier;
declare #TopMostNode uniqueidentifier;
BEGIN TRANSACTION;
select #UserID = NEWID();
select #ConfigID = newid();
insert into Users (UserID,Email,Password,CurrentConfig)
values(#UserID, #Email, #Password, #ConfigID);
INSERT INTO Configs (ConfigID, OwnerID, DisplayName, LastPrintID)
VALUES (#ConfigID,#UserID, 'Default Config', 1);
COMMIT TRANSACTION
END
That code is almost complete. Just need to add a SELECT to return the new keys. In case of an error, you will get an exception. EG:
ALTER PROCEDURE [dbo].[CreateUser]
#Email nvarchar(256),
#Password nvarchar(256)
AS
BEGIN
declare #UserID uniqueidentifier;
declare #ConfigID uniqueidentifier;
declare #TopMostNode uniqueidentifier;
BEGIN TRANSACTION;
select #UserID = NEWID();
select #ConfigID = newid();
insert into Users (UserID,Email,Password,CurrentConfig)
values(#UserID, #Email, #Password, #ConfigID);
INSERT INTO Configs (ConfigID, OwnerID, DisplayName, LastPrintID)
VALUES (#ConfigID,#UserID, 'Default Config', 1);
COMMIT TRANSACTION
select #UserID UserID, #ConfigID ConfigID;
END
Here is one possibility where you return a recordset with the data you require and a flag to indicate whether the user already existed or not.
alter procedure [dbo].[CreateUser]
(
#Email nvarchar(256)
, #Password nvarchar(256)
)
as
begin
declare #UserID uniqueidentifier, #ConfigID uniqueidentifier, #TopMostNode uniqueidentifier, #UserExists bit = 0;
select #UserID = UserID, #UserExists = 1 from Users where Email = #Email;
if #UserExists = 0 begin
begin transaction;
set #UserID = newid();
set #ConfigID = newid();
insert into Users (UserID, Email, [Password], CurrentConfig)
values (#UserID, #Email, #Password, #ConfigID);
insert into Configs (ConfigID, OwnerID, DisplayName, LastPrintID)
values (#ConfigID, #UserID, 'Default Config', 1);
commit transaction
end
-- Return whether the user already exists or not and the user id
select #UserExists, #UserId
return 0;
end
Another way to return data to the app is using output parameters e.g.
alter procedure [dbo].[CreateUser]
(
#Email nvarchar(256)
, #Password nvarchar(256)
, #UserId uniqueidentifier out
, #UserExists bit out
)
as
begin
declare #ConfigID uniqueidentifier, #TopMostNode uniqueidentifier;
set #UserExists = 0;
select #UserID = UserID, #UserExists = 1 from Users where Email = #Email;
if #UserExists = 0 begin
begin transaction;
set #UserID = newid();
set #ConfigID = newid();
insert into Users (UserID, Email, [Password], CurrentConfig)
values (#UserID, #Email, #Password, #ConfigID);
insert into Configs (ConfigID, OwnerID, DisplayName, LastPrintID)
values (#ConfigID, #UserID, 'Default Config', 1);
commit transaction
end
return 0;
end

Authenticate user in stored procedure

I am new to sql and I am not sure what's wrong with my stored procedure.
User inputs user name & password which are my input parameters and if it is correct then return 'Login Success', if UN is incorrect than return 'Incorrect UN' or if PW is incorrect than return 'Incorrect PW'. In the stored procedure I have an IF Else statement and it is only hitting the first IF statement not other.
Please have a look my stored procedure:
CREATE PROCEDURE [dbo].[AuthenticateUser]
#UserName varchar(15),
#Password varchar(15),
#Role varchar(25) OUTPUT
AS
SET NOCOUNT ON
BEGIN
DECLARE #UN VARCHAR(25)
DECLARE #PW VARCHAR(25)
SELECT #UN = UserName, #PW = Password FROM LogIn
IF (#UN != #UserName COLLATE SQL_Latin1_General_CP1_CS_AS)
BEGIN
SET #Role = 'Incorrect User Name'
END
ELSE
BEGIN
IF (#PW != #Password COLLATE SQL_Latin1_General_CP1_CS_AS)
BEGIN
SET #Role = 'Incorrect Password'
END
ELSE
BEGIN
SET #Role = 'Logged in Successfully'
END
END
SELECT #Role
END
Thank you for your help
You are doing this:
SELECT #UN = UserName, #PW = Password FROM LogIn
IF (#UN != #UserName COLLATE SQL_Latin1_General_CP1_CS_AS)
These comparisions #UN = UserName and #PW = Password should be made in the WHERE clause to help in proper filtering of rows.
Here is the code rewritten (you can change to using your own table name)
Drop Table TestLogin
GO
Create Table TestLogin
(
UserName VarChar (20),
Password VarChar(20)
)
Insert TestLogin Values ('One', 'Two')
GO
Drop PROCEDURE AuthenticateUser
GO
CREATE PROCEDURE AuthenticateUser
#UserName varchar(15),
#Password varchar(15),
#Role varchar(25) OUTPUT
AS
If ((SELECT Count (*) From TestLogin Where UserName COLLATE SQL_Latin1_General_CP1_CS_AS = #Username And Password COLLATE SQL_Latin1_General_CP1_CS_AS = #Password) = 0)
Begin
If ((SELECT Count (*) From TestLogin Where UserName COLLATE SQL_Latin1_General_CP1_CS_AS = #Username) = 0)
Begin
Select #Role = 'Incorrect User Name'
End
Else
Begin
Select #Role = 'Incorrect Password'
End
End
Else
Begin
Select #Role = 'Logged in Successfully'
End
GO
Declare #Role VarChar (100)
Exec AuthenticateUser 'One', 'Two', #Role Output
Print #Role
Exec AuthenticateUser 'One', 'TwoX', #Role Output
Print #Role
Exec AuthenticateUser 'OneX', 'Two', #Role Output
Print #Role
The three examples provided at the end show you how the procedure behaves when you give it a good login, or either parameter is incorrect.
Change it to a SELECT COUNT(1) FROM userLogin.... and then use ExecuteScalar() on the SqlDataReader object.
As a side note, it's not a good idea to store your passwords in the DB in plain text, but hash them instead, preferably with a salt value.
Try this instead:
Create PROCEDURE [dbo].[AuthenticateUser]
#UserName varchar(15),
#Password varchar(15),
#Role varchar(25) OUTPUT
AS
SET NOCOUNT ON
BEGIN
If Not Exists (Select 1 From LogIn Where UserName = #UserName) Set #Role = 'Incorrect UserName'
Else If Not Exists (Select 1 From LogIn Where Password = #Password) Set #Role = 'Incorrect Password'
Else Set #Role = 'Logged in Successfully'
Select #Role
END
Generally it is not good idea to give an attacker a cue "name is OK, now guess PWD". Plus, password should be at least case sensitive. For this purpose:
select #un=username from LogIn
where username=#username
and cast(password as varbinary(max)) = cast(#password as varbinary(max))
if #un is null
set #role = 'UN or PWD is incorrect'
else
set #role = 'Success'
If you want to give hints:
select #un=username from LogIn
where username=#username
if #un is null
set #role = 'UN not found'
else
begin
select #un=username from LogIn
where username=#username
and cast(password as varbinary(max)) = cast(#password as varbinary(max))
if #un is null
set #role = 'password incorrect'
else
set #role = 'Success'
end
P.S.: I hope username is unique in your table.

SQL Server: Use output parameter values in the same procedure also

I have this stored procedure:
CREATE PROCEDURE [dbo].[AddEmployee]
(
#employeeId int OUTPUT,
#firstName varchar(50),
#lastName varchar(50),
#password varchar(100)
)
AS
BEGIN
INSERT INTO Employees(FirstName, LastName)
VALUES(#firstName, #lastName)
INSERT INTO Logins(Password, EmployeeId)
VALUES(#password, #employeeId)
SELECT ##Identity
END
GO
Let,
#employeeId = EmployeeId
generated during insert in Employees
After that, I was trying to insert the #employeeId into the Logins table.
Also, the #employeeId is used as OUTPUT parameter.
How should I do it?
May be we can use INSERTED.EmployeeId, but I don't know how to use it.
CREATE PROCEDURE [dbo].[AddEmployee] (
#firstName VARCHAR(50)
, #lastName VARCHAR(50)
, #password VARCHAR(100)
, #employeeId INT OUTPUT
)
AS
BEGIN
INSERT INTO Employees (
FirstName
, LastName
)
VALUES (
#firstName
, #lastName
)
SELECT #employeeId = SCOPE_IDENTITY()
INSERT INTO Logins (
Password
, EmployeeId
)
VALUES (
#password
, #employeeId
)
END
GO
EXEC [dbo].[AddEmployee] #firstname = 'test'
, #lastname = 'tester'
, #password = '321321'
, #employeeId = ''
Assuming your EmployeeId column is an IDENTITY column, you should use the OUTPUT clause to fetch the value (you output it into a table variable):
DECLARE #empId TABLE (empId int)
INSERT INTO Employees
(
FirstName, LastName
)
OUTPUT INSERTED.EmployeeId INTO #empId
VALUES
(
#firstName,
#lastName
)
Then to get the value from the table variable into a scalar variable, do:
SET #employeeId = (SELECT TOP 1 empId FROM #empId)
The ##Identity value is the last identity inserted in the current session regardless of scope. Hence, it could be the identity of a row inserted by a trigger. It's always better to use scope_identity() instead. For more details, see MSDN.
You can assign an output parameter like:
set #employeeId = scope_identity()

SQL Server error.Explicit value for Identity Column....can only be specified when a column list is used and IDENTITY_INSERT is ON

I am trying to create procedure, which is generating an error stating
An explicit value for the identity column in table tblRegisterUser can only be specified when a column list is used and IDENTITY_INSERT is ON.
I tried to surround insert statement with INDENTITY_INSERT to ON,but that too doesn't work. am I missing anything or is it an error with the sub query which i included?
Following is the stored procedure
CREATE PROCEDURE dbo.spInsertUserRegister
(
#FirstName nvarchar(50),
#LastName nvarchar(50),
#Username nvarchar(50),
#Password nvarchar(50),
#Designation nvarchar(50),
#Department nvarchar(50),
#IsAdmin bit
)
AS
BEGIN
INSERT INTO tblRegisterUser Values
(
#FirstName, #LastName, #Username, #Password,#Designation,#Department,#IsAdmin
)
DECLARE #UID INT
SET #UID = ##IDENTITY
INSERT INTO tblLogin(Username,Password,UID,IsAdmin)
Values(#Username, #Password, #UID,(SELECT IsAdmin FROM tblRegisterUser WHERE Username=#Username AND Password=#Password))
END
If the structure of the tblRegisterUser table is something like
ID int primary_key autoincrement
FirstName varchar
LastName varchar
Username varchar
Password varchar
Designation varchar
Department varchar
IsAdmin bit
than this statement is wrong:
INSERT INTO tblRegisterUser Values
(
#FirstName, #LastName, #Username, #Password,
#Designation,#Department,#IsAdmin
)
You should use an explicit column list to specify the columns:
INSERT INTO tblRegisterUser
( FirstName, LastName, Username, Password,
Designation, Department, IsAdmin)
VALUES
(
#FirstName, #LastName, #Username, #Password,
#Designation,#Department,#IsAdmin
)
This way the ID field is automatically populated, and the ##Identity statement should return it correctly.
That said, SQL Server has a few functions that return the generated ID for the last rows, each with it's own specific strengths and weaknesses.
Basically:
##IDENTITY works if you do not use triggers
SCOPE_IDENTITY() works for the code you explicitly called.
IDENT_CURRENT(‘tablename’) works for a specific table, across all scopes.
In almost all scenarios SCOPE_IDENTITY() is what you need, and it's a good habit to use it, opposed to the other options.
A good discussion on the pros and cons of the approaches is also available here.
copied from this answer
Try this one -
CREATE PROCEDURE dbo.spInsertUserRegister
(
#FirstName NVARCHAR(50),
#LastName NVARCHAR(50),
#Username NVARCHAR(50),
#Password NVARCHAR(50),
#Designation NVARCHAR(50),
#Department NVARCHAR(50),
#IsAdmin BIT
)
AS BEGIN
INSERT INTO dbo.tblRegisterUser (FirstName, LastName, Username, [Password], Designation, Department, IsAdmin)
SELECT #FirstName, #LastName, #Username, #Password, #Designation, #Department, #IsAdmin
DECLARE #ID BIGINT
SELECT #ID = SCOPE_IDENTITY()
--SET IDENTITY_INSERT dbo.tblLogin ON;
INSERT INTO dbo.tblLogin (UserName, [password], [uid], IsAdmin)
SELECT #Username, #Password, #ID, IsAdmin
FROM tblRegisterUser
WHERE UserName = #Username
AND [password] = #Password
--SET IDENTITY_INSERT dbo.tblLogin OFF;
END

Resources