Authentication Principle » History » Revision 2
Revision 1 (Eric Vieillevigne, 05/12/2015 01:50 PM) → Revision 2/3 (jerome bonnet, 05/12/2015 03:27 PM)
h1. Authentication h1. 1 Principles h2. 1.1 Account In the platform a user having family data is called an account. An account may be authenticated on the platform and has: * One or several "account identifiers" * A Password: Password of a user is stored in the databased with a BCrypt Hash h2. 1.2 Account identifiers Each user's account on the platform is identified by one or several identifiers. Identifier properties are: * The 'type', which is an enum that may be: > * Email: an email > * Msisdn: a phone number (not used at the moment) > * ExternalId: the id of an external system (for SSO integration) > * Login: a login (free-form string) > * AccessToken: a random string being an identifier for a cryptographic authentication token > * SOMETHING_ELSE: reserved for extension mechanism. * The 'value', which correspond to the type and must be totally unique across all users h2. 1.3 Type of Authentication The server APIs falls into two different categories: - _*Account-based api*_ : > API where the API api call is relative to an account, such as wall/get (retrieve wall messages). Such APIs apis do always work on the user's account data or on the behalf of the Account, and the account in question is not passed as an explicit argument to the API. api. It is an implicit argument. > Such APIs apis mandate the caller to be authenticated and that the authentication corresponds to an account on the platform. - _*Non-account-based api*_ , such as createAccount: > API which does not consider the logged account as an implicit argument. Such APIs apis may mandate the client (or the calling application) to be authenticated, but does not mandate that the authentication correspond to an account on the platform. For instance, the client, or an administrator, shall may be authenticated to perform such API api calls, but they do not automatically correspond to an account. As a result, an API api call can be authenticated in two fashions: - _*Client Authentication*_ > Authentication recognizes recognize the calling application and its associated rights right to call this API. one or another api - _*Account Authentication*_ > Authentication recognizes recognize the account and therefore enable calling account-based APIs. api. > Both authentication are enforced can be available at the same time for each incoming request to the server. time. h2. 1.4 Client authentication h2. 1.4.1 API Api Access Right Ruleset The An Access Right ruleset is a set of rules indicating if each API api is available or not depending on the client authentication. not. For instance the ruleset "WEB" indicate that most of the APIs API are accessible for clients authenticated as "WEB" by this ruleset but that some administration APIs api are not. Those rulesets are configured in the resource file accessrights.xml h2. 1.4.2 Clients Id and Secret Clients are authenticated by a *clientId* and a *clientSecret*, similarly as a login/password for a user. Each clientId client are allocated a given set of access rights that will correspond to: # Access Right RuleSet : a configured set of access right to indicate the authorized and forbidden APIs apis # Client-type : Confidential or Public, indicating if the client is "internal" to FamilyWall or external. # RedirectUrl : start of the authorized url redirection, needed for the web authentication. authentification # isOAuth2PasswordBasedAuthorized: indicate if the client is authorized to transport login/password information of the user, or required to go through the authorization server. user # name: the real name of the client Clients are authenticated with Oauth2 in a standard way and with WEB authentication. Legacy authentication does not support client authentication. h3. 1.4.3 Client authorization errors Whenever an API is called without client authentication authentification and is not in the NOAUTHENT Access Right ruleset, the following error is returned to the caller: 501 : FizAccountNotFoundInSessionException (account-based authentication required) 509 : FizApiKeyInsufficientRightsException (client-based authentication required) h3. 1.4.4 ClientId HTTP authentication While performing an HTTP authentication, clientId and ClientSecret may be send in the HTTP request in several way > using HTTP parameters, either in post or in get, either in the URL or in an url-encoded part (single part or multipart). For instance: <pre> GET /api?a01call=xxxxxx&client_id=1-2-3-3-2&client_secret=azerty HTTP/1.1 </pre> > using HTTP Authenticate header, with a basic scheme. The value of the header shall be the concatenated string of clientId and clientSecret separated by a colon and base64-encoded. For instance: <pre> POST /token HTTP/1.1 Authorization: Basic MS0yLTMtMy0yOmF6ZXJ0eQ== </pre> And MS0yLTMtMy0yOmF6ZXJ0eQ== is the base64-encoding of 1-2-3-3-2:azerty , 1-2-3-3-2 being the clientId and azerty being the clientSecret. h2. 1.5 HTTP Sessions The API api support (and even requires for performance reason) a session concept over HTTP. At any time during an HTTP API api call the server _MAY_ respond with an additional Set-Cookie HTTP header, setting a JSESSIONID http cookies. At any subsequent call the the server, the client _MUST_ return the JSESSION id cookie, and ONLY this cookie. The cookie time-to-live on the client may be short and shall, if possible, correspond to a user session, if applicable. At any time the server may invalidate an HTTP session and resend a JSESSIONID. However the server shall implement an HTTP session time-to-live based on a 30 minutes timeout after the latest lastest access. h1. 2 LEGACY authentication based on the http session > This Authentication scheme will be deprecated in the future, because it does not support Client Authentication, and is poorly secure. This authentication scheme consist in calling the log2in api priori to any other account-based API. Non account-based apis are left unauthenticated. By calling the log2in api, the identifier/password are passed to the system and verified. The identifier must correspond to an account's identifier and the password must pass the hash test of the corresponding account. If the authentication is successful, the current HTTP session will become Account Authenticated up to the end of the HTTP Session. The api log2out may be called to remove the account authentication from the current HTTP Session. This will also invalidate the current HTTP session. Because an http session can be invalidated by the server at any time (or almost at any time, the server _SHALL_ implement a 30 minutes timeouts after the last call on a session), the client _SHALL_ be ready to manage any 501:FizAccountNotFoundInSessionException errors and redo a log2in call. When this authentication scheme is used, the Access Right Ruleset used (indicating which api can be called and which cannot) is LEGACY h2. 2.1 Authorization errors: Whenever an account-based api is called without an account authentication, the following error will be returned to the caller: 501 : FizAccountNotFoundInSessionException h2. 2.2 ISSUES WITH HTTP SESSION AUTHENTIFICATION *MERGE METHOD* : merge method in account are not compatible with header auths *RESET PASSWORD* : must be authenticated with a email token, which is one-time use token *INVITATION* : must be authenticated with an email token, which may be used several time during a given period *CREATEACCOUNT* : create account API shall return an AccessToken instead of letting the HTTP SESSION API Alive *PREVIOUSLOGINDATE* : We store the PREVIOUS LOGIN DATE in the http session to perform some magic, this must be REMOVED However this is used in the WEB for the numbers in the notification badge in the family menu, this MUST be redesigned anyway (or not ?) it can still be used if it is only on the WEB. *LASTLOGINDATE* : we need to change to policy of this update WE MUST MOVE THE CODE FROM AccountLoginApiImpl.loginAccountFromIdentifier to somewhere else... h1. 3 OAuth2 Authentication h2. 3.1 OAuth2 Presentation The system is able to use various (but not all) OAuth2 authentication scenarios to provide access to account-based APIs. apis. > Based on The OAuth 2.0 Authorization Protocol (draft-ietf-oauth-v2-31) (see http://datatracker.ietf.org/doc/draft-ietf-oauth-v2/ ) The principle is that the client must obtain an Access-Token and a Refresh-Token from the server, and then use the Access-Token to authenticate itself and use account-based APIs. apis. In order to obtain the Access-Token and the Refresh-Token, the client must use HTTPS/TLS. The type of access-token managed by the server is "MAC Access Authentication" or "BEARER Access Authentication". . > Based on HTTP Authentication: MAC Access Authentication, draft-ietf-oauth-v2-http-mac-01 (see http://datatracker.ietf.org/doc/draft-ietf-oauth-v2-http-mac/ ) > Based on HTTP Authentication: Bearer : draft-ietf-oauth-v2-bearer-22.pdf (see http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-23 ) The MAC Access Token is secure enough in order to be used on plain http connection instead of HTTPS/TLS. However his time-to-live is medium-lived (ie several days). When an Access Token is obsolete, the client must use the Refresh Token to obtain a new Access Token, using HTTPS/TLS. The Refresh Token is much more long lived (several years). The Access-Token is used by signing an http request with the token secret with a hmac algorithm and put the signature in the Authorization Header of the http request. The > Based on HTPT Authentication: Bearer Access Token is not secure enough to be used over plain http connection, therefore when using a bearer token, using HTTPS/TLS is mandatory when performing API calls. : draft-ietf-oauth-v2-bearer-22.pdf (see http://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-23 ) > Note that this authentication mechanism does not alleviate aleviate the client from handling the HTTP Sessions cookies. However the HTTP sessions will be transparently recreated automatically if and re-authenticated and the session cookie usage of the log2in api is missing from the client. not necessary anymore. In order to retrieve the Access-Token or the Refresh-Token, several scenario are possible, depending on the type of application, the type of client, etc... The following scenario are implemented: > Exchange against login/password (for privileged privilegied clients) The client must use the " log2generateaccesstokenbypassword " api to exchange the login/password of the user with an access token. > Exchange against a validation token (for privileged privilegied clients) The client must use the " log2generateaccesstokenbyvalidationtoken " api to exchange a validation token with an access token, the validation tokens may come from privileged SSO integrations. > Exchange against an authorization code (for non-privileged clients) The client must obtain an authorization code from the authorization server, enabling secure third-party authentication. > Privileged clients are recognized because their isOAuth2PasswordBasedAuthorized flag is set to true. token. h2. 3.2 OAuth2 error codes When an access token is required and missing the following error is returned to the client: <pre> HTTP/1.1 401 Unauthorized WWW-Authenticate: MAC </pre> or <pre> HTTP/1.1 401 Unauthorized WWW-Authenticate: Bearer </pre> When an access token is obsolete the following error is returned to the client: <pre> HTTP/1.1 401 Unauthorized WWW-Authenticate: MAC Expired </pre> For other errors the WWW-Authenticate will provide various error messages. h2. 3.3 Compatibility with authentication based on the http session Normally, resource protected by OAuth will deny access of any request that do not have a valid Authorization Header. However, when using the "Simple login/password authentication based on the http session" (see #2.0) concurrently, we have the following policy: For accessing account-based apis: # Any http request passed with an http session identified authentified with the "Simple login/password authentication based on the http session" will not require the OAuth 2.0 authentification # Any other http request will requires the OAuth Authorization http header For non-account-based apis, for the moment they are left unauthentified while we implement security on non-account based apis. h2. 3.4 Getting an Access Token for privileged privilegied application h3. log2generateaccesstokenbypassword The client must: > > Provide a client_id and client_secret to identify itself (using basic_auth or client_id and client_secret HTTP http parameters) > > Provide a deviceId (as in the devicesetconfig api) to identify the device. device > > The type of desired token (either Bearer or Mac) > > Must perform his call using HTTPS Example: <pre> GET /api?a01call=log2generateaccesstokenbypassword&a01identifier=margesimpsontest&a01password=marge&a01tokentype=Bearer&a01deviceId=1-2-3-4-5&client_id=1-2-3-3-2&client_secret=azerty HTTP/1.1 </pre> In this example, the client gets his accessToken by using the user's credentials margesimpsontest/marge . > His device is identified by 1-2-3-4-5 > The client key is 1-2-3-3-2 / azerty > The requested token type is "Bearer" For the response, see 3.6 h3. log2generateaccesstokenbyvalidationtoken The client must: > > Provide a client_id and client_secret to identify itself (using basic_auth or client_id and client_secret http parameters) > > Provide a deviceId (as in the devicesetconfig api) to identify the device > > The type of desired token (either Bearer or Mac) > > Must perform his call using HTTPS Example: <pre> GET /api?a01call=log2generateaccesstokenbyvalidationtoken&a01validationtoken=123token123&a01tokentype=Bearer&a01deviceId=1-2-3-4-5&client_id=1-2-3-3-2&client_secret=azerty /api?a01call=log2generateaccesstokenbyvalidationtoken&a01validationtoken=123token123&a01tokentype=Bearer&a01deviceId=1-2-3-4-5&client_id=1-2-3-3-2&client_password=azerty HTTP/1.1 </pre> In this example, the client gets his accessToken by using the validation token 123token123 . > His device is identified by 1-2-3-4-5 > The client key is 1-2-3-3-2 / azerty > The requested token type is "Bearer" For the response, see 3.6 h2. 3.5 Refresh Token usage In order to use the refresh token, the client must present the refresh token to the /token endpoint in HTTPS/TLS. See #6 of IETF OAuth specification. Example: <pre> POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded;charset=UTF-8 grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA </pre> Note: Authorization headers shall contains the clientId/clientSecret for the client authorization. For there, all access performed with the Access-token will be considered as client-authenticated with those credentials. It is mandatory. h2. 3.6 Access-Token response format As defined in both ietf specification, the access token will be returned in JSON. The only type of access-token managed is "mac". The only type of max algorithm managed is "hmac-sha-1". <pre> HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"mac", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "mac_key":"AEZRDF41424aezraezr", "mac_algorithm","hmac-sha-1" } </pre> h2. 3.7 Using the Bearer access-token to generate Authorization request. It is fairly simple. You just need to add an http basic-auth authorization header. Example: <pre> POST /API HTTP/1.1 Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA aezraezrez:eazrraezraezraez </pre> h2. 3.8 Using the MAC SHA1 access-token to generate Authorization request. It is actually all explained here : draft-ietf-oauth-v2-http-mac-01 , but I will summarize the procedure here: # Generate the request Timetsamp in seconds # Generate a random number to be used as a nonce # From the request being done, extract the HOST, PORT, HTTP_METHOD, HTT_METHOD, and FULL_PATH_OF_QUERY # Concatenate all Timestamp, nonce, and information extracted from request into a string # Compute the hmac of the concatenated string with the mac_key # Generate and add an Authorization header in the http request The format of the Authorization header is: <pre> Authorization: MAC id="h480djs93hd8", ts="1336363200", nonce="dj83hs9s", mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM=" </pre> The id is the "access_token" value, the ts is the timestamp, the nonce is (well) the nonce, and the mac is the result of the hmac computation. h1. 5 Third-party WEB Oauth Authentication This Authentification Web application authentication method enables workflow is a third-party application derivated version of the oAuth2 protocol, but that does not requires the Authorization server to access a user's account on his behalf, after handle the user has specifically authorized display of the application webpage himself. The idea is to perform such access. The third-party authentication rely on a web-based technology for mandate that the authorization servers client is authenticated (via his clientId/clientSecret) without exposing the clientSecret to authenticate the user and deliver the authorization. browser (note that in oAuth browser=user-agent). In this setup order to illustrate the involved entities are: process, we take the following hypothesis: # User: User-Agent is a firefox browser, and it runs the user main load of the third-party application and FamilyWall application developped in Javascript. # User-Agent: Webapp is build with PHP, and holds the client application the user have secret. The secret is protected because it is sent to access both applications, capable or rendering web-pages (ie a browser or a native application rendering a web-view) # TPY Server: the third-party server who wants to access the user's account on the FamilyWall server # FamilyWall authorization server: the FamilyWall authorization server who will authenticate the user and ask for his permission before delivering the autorization. User-Agent. <pre> +----------+ | User | | | +----------+ ^ | (C) +----|-----+ Challenge authorize User Login/Pwd +---------------+ | +----(C)-- +----(D)-- return login form JSESSIONID ---->| FamilyWall | | User- | | Authorization FizApi | | Agent +<---(D)-- Challenge response ----| +<---(E)-- JSESSIONID ------------| Server | | | | | | | | | +-|----|---+ +---------------+ | | ^ v (A) (D) (F) | | | | | | ^ v | | +---------+ | | | |>---(B)-- Request Authorize ----------' State Random Cypher---------' | | TPY PHP | Response Challenge authorize (redirect) return StateSecret | | Server | | | |>---(E)-- generate access token with code ---' |>---(G)----- JSESSIONID -------------------' +---------+ (client ID + Secret) </pre> The steps are: h2. A: Through User-Agent performs the User-Agent, the user is authenticated first request to the TPY PHP server and trigger an authorization request. h2. B: The TPY server is requesting an authorization procedure to retrieve all the FamilyWall Authorization Server Javascript code of the webapp. B: In order to initialize this authorization request, Javascript, the TPY PHP server sends: # his client_id # is asking the parameter response_type with value equals "code" # FizApi server to generate a state parameter for his own usage sessionSecret from a State Random Cypher (random generated by PHP) # A end: The PHP server is embedding the redirect_uri, that must match what is defined StateSecret in his client authentication the Javascript code and his client_id in "RedirectUrl" (ie redirect_uri must starts with RedirectUrl) Example: <pre> GET /authorize?response_type=code&state=xyz&client_id=1-2-3-3-2&redirect_uri=https%3A%2F%2Fwww%2Etpyserver%2Ecom%2Freturn HTTP/1.1 </pre> The response consists in an HTTP redirect 302 returns the Javascript code to the FamilyWall authorization server. Example: <pre> user-agent HTTP/1.1 302 Found Location: https://api.familywall.com/challenge?jsessionid=D6C709DCDDAEC347A8C4E51620F4CD20.pre1 </pre> Note that Because the authorization server sessionSecret is using a session id parameter to set-up a server-side context of embedded in the authorization. The TPY server Javascript, it will render screen-scraping more difficult, it shall send this redirect back to be buried deep inside the user-agent, and the user-agent will follow the redirect. Javascript, as obfuscated as possible. h2. C: The User-Agent will fetch Javascript within the authorization challenge user-agent is presenting a "html form" to the user asking for his login/password and return a login form directed to the FizApi Server The Login form contains the name of the TPY server and State Random Cypher The user is explicit about the requested authorization. submitting his login/password D: The user-agent will display is submitting the form to the user, and FizApi Server (directly without Javascript for Safari compatibility) Containing the user will authenticate to the FamilyWall server login, password, and authorize the TPY server to access his account. State Random Cypher. h2. D: E: The Familywall Authorization server return FizApi Server is validating the authorization code If the user successfully authenticate login/password, if it is valid, it opens a WEB api session and authorize, The FamilyWall Server register this authorization and return an authorization code the session identifier to the TPY server: User-Agent. The session is still not client authenticated. <pre> HTTP/1.1 302 Found Location: https://www.tpyserver.com/return?code=adienaEEFez1348233ZEFGH123AcTT&state=xyz </pre> F: The TPY server can retrieve Javascript in the authorization code user-agent is submitting to PHP the JSESSIONID and his private state. the StateSecret h2. E: G: The TPY server exchange PHP is submitting the authorization code for an access token and a refresh token The client must: > > Provide a client_id and client_secret triplet (StateSecret,JSESSIONID,clientId+ClientSecret) in order to identify itself, and it must match validate the one used JSESSIONID on the authorization code FizApi Server > > Provide the authorization code > > The type of desired token (either Bearer or Mac) > > Must perform his call using HTTPS Example: <pre> GET /api?a01call=log2generateaccesstokenbyauthorizationcode&a01code=adienaEEFez1348233ZEFGH123AcTT&a01tokentype=Bearer&client_id=1-2-3-3-2&client_secret=azerty HTTP/1.1 </pre> FizApi Server is validating that 1) The response is StateSecret corresponds to the same as State Random Cypher for the other apis, see "Access-Token response format". same JSESSIONID, 2) the clientId+ClientSecret are valid The FizApi Server grants the session with the JSESSIONID with corresponding client Authorization. Any calls made by the user-agent to the FizApi server with this JESSIONID will be authorized.