How to uniquely identify user logging in via oauth? - oauth

I particular - I don't understand how to link user that authenticated using oauth to a particular account in my application?
So here's accounts in my applciation:
CREATE TABLE accounts (
id BIGINT NOT NULL AUTO_INCREMENT,
username VARCHAR(40),
email VARCHAR(256),
created DATETIME,
updated DATETIME,
PRIMARY KEY (id),
UNIQUE KEY (email),
UNIQUE KEY (username)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;
With openid for example there is a unique user id (uri, xri) which uniquely identifies that user. So I can just link to my accounts like this:
CREATE TABLE openid_logins (
id BIGINT NOT NULL auto_increment,
fk_accounts_id BIGINT NOT NULL,
openid_identity TEXT NOT NULL, /*that's unique user id*/
openid_provider_url VARCHAR(255) NOT NULL, /*flickr, yahoo, live_journal*/
PRIMARY KEY (id),
INDEX (openid_identity),
FOREIGN KEY (fk_accounts_id) REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE
);
So Whenever user logs in via openid -> I can get his regular account referencing fk_accounts_id.
But when it comes to oauth - AFAIK there is no such things as oauth_identity_string... And since oauth tokens might change tokens by themselves cannot be used as a unique link to profile in my applicaiton..... So what should I do? How to uniquely identify a user logging in via oauth?

You can't get information about the user by the OAuth protocol itself, however, there is normally an endpoint, to which you can make a request, that provides user information. For example Google provides one: after you receive your token, you can make a request to:
GET https://www.googleapis.com/plus/v1/people/me?access_token={TOKEN}
This will return a JSON object containing information about the user, including an unique identifier.

Get the account_id using the apis and store it in the following format:
-- --------------------------------------------------------
--
-- Table structure for table oauth -- its like a username passwd thingy
--
CREATE TABLE IF NOT EXISTS oauth (
oauth_id bigint unsigned NOT NULL AUTO_INCREMENT UNIQUE,
service varchar(16) NOT NULL,
account_id varchar(256) NOT NULL,
token text NOT NULL,
token_secret text NOT NULL,
PRIMARY KEY (service, account_id)
) ENGINE=InnoDB DEFAULT CHARSET=ascii COLLATE=ascii_bin;
service can be a google, twitter, etc if you are using multiple oauth

Related

unable to add claim to id or access token based on attributes column of accounts table

After many tests I can't figure out how to add claim to userinfo / id token / access token based on the SCIM 2.0 format for the attributes column inside accounts table.
Curity 6.6.0
Mysql configured as the default datasource also for user management
Mysql attributes query configured in the datasource : SELECT * FROM accounts WHERE username = :subject
User added through the User SCIM endpoint have their data inside the 'attributes' column of the accounts table (seems to be by design when the onyl one DS exists in Curity)
family_name, given_name appears correctly in the id token as the connected user
I'm using BFF Token handler SPA code (nodejs) with the overall flow validated
Step by step :
Create a claim named 'user_type'
select the default-account-manager associates with a claim provider
in the 'select value' combo search and add userType (all attributes of the SCIM 2.0 schema are referenced in the combo)
Add the claim to a custom scope & profile scope
Configure the claim to be in the userInfo & Id Token & access token
Commit
--> No user_type field in any token or userInfo
I have tried with (return attributes.userType) and without any mapping, but no way...
The only working test is the one with a mock : return {userType: 'test'} or return 'test'.
It seems that the attributes query SELECT * FROM accounts WHERE username = :subject does'nt allow to use children fields of the attributes column.
Despite the fact that it seems to work correctly in https://curity.io/resources/learn/claims-from-authenticated-subject/ subject attributes in authentication with code
function transform(attributes) {
//Transform the attributes and return the appropriate value for the claim
if(attributes.emails !== undefined
&& attributes.emails.length > 0
&& attributes.emails[0].value !== null) {
//return {"email" : attributes.email};
return attributes.emails[0].value;
}
return null;
}
Any help will be greatly appreciate to point out what I'm doing wrong
Sounds like you got most of it right, but some details to tweak.
First, to use the attribute query, you can't use an Account Manager claims provider, that one uses the account query for the lookup and is not configurable. Instead, use the Data Source Claims Provider.
Second, the claim config can't query in multiple steps, so you'll need to transform your result.
In the picture below, I'm using a data source provider with your query, and putting the value from my attributes.timezone in the "email" claim.
Note the logger.warn, it's useful to find out what you get back from the datasource.

userType field - which values are allowed?

Microsoft Graph API's User entity has field "userType". According to documentation there is no any information about this. There is one line: "A string value that can be used to classify user types in your directory, such as “Member” and “Guest”. Supports $filter.". So there is no any limitations :-)
Can be "userType" null? I can't PATCH existing user manually via graph api - graph api return an error. But on production we have some users, which have "userType": null
Which values are valid for "userType"? Can you provide it in documentation or here please?
According to your questions, I suppose you want to know the valid value of the field userType. we can refer to the content of User Entity.
For your first question, we can set the field userType to null, not " " or "null". When we create a user, the default value of this field will be Member if we didn't set it.
We can patch existing user. Based on my test, we can modify it like this:
PATCH https://graph.microsoft.com/v1.0/users/{userid}
{
"displayName": "XXX",
"givenName": "XXXX",
"jobTitle": "Marketing Director",
"userType": "Guest"
}
This will modify the user'userType from Member to Guest.
For your second question, according to the content of User Entity and the article of Azure AD User, the valid value for userType is "Member" and "Guest". Based on my test, it is sure that only these two values can be used.

multilingual database schema for mvc dot.net model

We had developed in the past some sites, from company presentation sites to eshops, in classic asp. All of these was developed in multilingual environment (el, en) most of them. From database view we had choose the following schema. For example, for products table we have two related tables one with no lingual depended fields and one for lingual depended fields with one to many relation.
CREATE TABLE ref_language (
Code Char(2)NOT NULL,
Name Varchar(20) NOT NULL,
PRIMARY KEY (Code)
);
CREATE TABLE app_product (
Id Int IDENTITY NOT NULL,
PRIMARY KEY (Id)
);
CREATE TABLE app_product_translation (
ProductId Int NOT NULL,
LanguageCode Char(2) NOT NULL,
Description Text NOT NULL,
FOREIGN KEY (ProductId) REFERENCES app_product(Id),
FOREIGN KEY (LanguageCode) REFERENCES ref_language(Code)
);
To recreate the product model we use stored procedures to join the two tables for the requested language.
Now we want to move into dot.net mvc model. And we are wondering if there is a better approach, most suitable in mvc model.
It depends on your requirements, but you can just create a class to load and cache the captions in memory, loading them using EF. Either use a static class or preferably the ASP.NET cache.
If you are doing dynamic stuff on the client side then expose the strings through a MVC of WebAPI controller if necessary.
To make this easier I would decouple your translations from products in the schema. Make your translations universal.
Just have a single table called app_translation with Id, LanguageCode and Translation field. Then reference the Id on any table that needs a translated caption.
To enforce referential integrity, you can also have a app_translation_identifier table with a single column and a unique constraint. Then FK from the app_translation.Id to app_tranlsation_identifier.Id. And also have a unique key on app_translation for the Id and LanguageCode.

Using Claims Types properly in Owin Identity and Asp.Net MVC

I am using Owin and Identity and I am having a problem with Claims.
I have applications where users use an email to authenticate and others that use an username.
The sign in method in the business layer can accept an Email or an Username depending on the case.
To "obfuscate" the user identity I use a GUID, unique to each user, when displaying a page with user info.
I also use this because sometimes an email or an username can be a problem in the url ...
When I sign a user I have the following claims types:
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Name, user.FullName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
new Claim(ClaimTypes.NameIdentifier, user.UserUniqueIdentifier.ToString())
So my interpretation is:
Email is the user's email
Name is the user's full name
GivenName is the user's first name
Surname is the user's last name
NameIdentifier is the user's unique identifier ... It can be the email, the username or in this case I am using an Unique ID.
What is strange is there is no Claim Type for Username. Where would to place it?
Basically it seems there is a problem when a Username is not used as the Unique name identifier but it is still necessary.
Is something wrong with my logic claims types?
ClaimTypes.Name (http:// schemas.xmlsoap.org/ws/2005/05/identity/claims/name) should be used for the username.
ClaimTypes.NameIdentifier is typically used for the user's id. In some cases it could be a username.
ASP.NET Identity uses ClaimTypes.Name to store the username, and ClaimTypes.NameIdentifier to store the primary key GUID of the user.
If you examine what Facebook or Google return from oAuth you will see that ClaimTypes.Name is ClaimTypes.GivenName + ClaimTypes.Surname. LinkedIn returns then concatenated and I believe this is a bug because I have a completely different username there. Twitter returns username for ClaimTypes.Name, but Twitter is a special case and they do not even return email.
All of them are using some opaque numeric identifier for ClaimTypes.NameIdentifier. And they use their own string names, usually starting with urn:facebook:link, urn:google:profile, etc for custom data.
Asp.NET Identity model uses UserName for ClaimTypes.Name. The bottom line is that ClaimTypes.Name is used differently in practice. You could add any claim name as a string and could add the urn:... scheme to make it unambiguous.

Yii Localisation - CDbMessageSource example

I'm newbie to Yii. Official documentation does not give any examples for CDbMessageSource.
Questions:
1) How/Where do I set CDbMessageSource as my MessageSource ?
2) In my current application I store Categories in one table and translations for Categories in other table. Tables structure:
CATEGORY
----------
cat_id (PK)
CATEGORY_TRANSLATION
--------------------
cat_id (FK)
en
ru
Now if I introduce sub-categories I would model DB this way:
SUB_CATEGORY
------------
sub_cat_id (PK)
cat_id (FK)
SUB_CATEGORY_TRANSLATION
------------------------
sub_cat_id (FK)
en
ru
Do I understand it correctly that in Yii if I want to use CDbMessageSource to store translations then I would need to merge CATEGORY & SUB_CATEGORY in to one table , then merge CATEGORY_TRANSLATION & SUB_CATEGORY_TRANSLATION in to other so that in result I get following structure (taken from here http://www.yiiframework.com/doc/api/1.1/CDbMessageSource) :
CREATE TABLE SourceMessage
(
id INTEGER PRIMARY KEY,
category VARCHAR(32),
message TEXT
);
CREATE TABLE Message
(
id INTEGER,
language VARCHAR(16),
translation TEXT,
PRIMARY KEY (id, language),
CONSTRAINT FK_Message_SourceMessage FOREIGN KEY (id)
REFERENCES SourceMessage (id) ON DELETE CASCADE ON UPDATE RESTRICT
);
Thank you !
How to enable CDbMessageSource
The message source is an application component with the name "messages". Therefore you configure it just like any other component in your application configuration file:
array(
......
'components'=>array(
......
'messages'=>array(
'class'=>'CDbMessageSource',
// additional parameters for CDbMessageSource here
),
),
),
)
The message source and localizable models -- not an ideal relationship
It's important to keep in mind that the message source only provides translations for known messages. It does not make much sense to involve the message source in your model localization because how would you utilize it?
Assume you have a category with id = 1. How would you get its localized title? Something like Yii::t('category', 'title_'.$category->id) could work, but it's somewhat clumsy (not desirable syntax, you have to "bake in" your primary key information into your display code, etc). If your title localizations are also meant to be modifiable by users this is going to get even more complicated. (In any case, if you wanted to do this then merging the two translation tables and using a separate value when populating SourceMessage.category would be the way to go).
An alternative approach to localizing models
Here's a brief rundown of how you can conveniently localize your models. Let's say we have a Room model with a localizable name property. You can create a new table named LocalizedString and the corresponding model that has a structure similar to this:
CREATE TABLE IF NOT EXISTS `localized_string` (
`Id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`LocaleCode` char(5) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`StringTemplate` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`Id`,`LocaleCode`),
);
Then, configure your Room model with a relation on LocalizedString:
public function relations()
{
$localeCode = Yii::app()->getLanguage();
return array(
'nameStringTemplate' => array(
self::BELONGS_TO, 'LocalizedString', 'NameStringTemplateId',
'condition' => 'nameStringTemplate.LocaleCode = \''.$localeCode.'\''),
);
}
And add a read-only property:
public function getName() {
return $this->nameStringTemplate->StringTemplate;
}
By doing this, you can now write $room->name anywhere and you will automagically get back the localized translation for the application's current language.
There are many details that need to be taken care of and that I have glossed over here, but the idea should be apparent.

Resources