public void SerializeDeserializeAppPrincipal()
        {
            AppPrincipal appPrincipal             = new AppPrincipal("genericAppHandle", "genericAppKey");
            string       serializedAppPrincipal   = appPrincipal.ToString();
            AppPrincipal deserializedAppPrincipal = new AppPrincipal(serializedAppPrincipal);

            Assert.AreEqual(appPrincipal.AppHandle, deserializedAppPrincipal.AppHandle);
            Assert.AreEqual(appPrincipal.AppKey, deserializedAppPrincipal.AppKey);
        }
        /// <summary>
        /// Method that generates the token descriptor of a SocialPlus session token
        /// </summary>
        /// <param name="appPrincipal">app principal</param>
        /// <param name="claims">token's claims</param>
        /// <param name="duration">lifetime of the token</param>
        /// <returns>token descriptor</returns>
        private SecurityTokenDescriptor GenerateSessionTokenDescriptor(AppPrincipal appPrincipal, Claim[] claims, TimeSpan duration)
        {
            var now = DateTime.UtcNow;

            return(new SecurityTokenDescriptor
            {
                TokenIssuerName = appPrincipal.ToString(),
                Subject = new ClaimsIdentity(claims),
                Lifetime = new Lifetime(null, now.Add(duration)),
                SigningCredentials = new SigningCredentials(this.signingKey, SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256Digest),
            });
        }
Example #3
0
        /// <summary>
        /// Validate authorization header.
        /// The SocialPlus auth header must include a token. This token is our own session token that includes an app and a user principal
        /// This method:
        ///     - validates the token
        ///     - validates the app principal
        ///     - validates the user principal
        /// </summary>
        /// <param name="authParameter">authorization parameter</param>
        /// <returns>list of principals</returns>
        public async Task <List <IPrincipal> > ValidateAuthParameter(string authParameter)
        {
            string token;

            // Parse the authorization header
            var authDictionary = new Dictionary <string, string>();

            this.ParseAuthParameter(authParameter, authDictionary);

            // Get the token and validate it.
            authDictionary.TryGetValue("tk", out token);
            var principals = await this.sessionTokenManager.ValidateToken(token);

            // Before returning the principals, we must validate them
            AppPrincipal  appPrincipal  = null;
            UserPrincipal userPrincipal = null;

            foreach (var p in principals)
            {
                if (p is AppPrincipal)
                {
                    appPrincipal = p as AppPrincipal;
                }
                else
                {
                    userPrincipal = p as UserPrincipal;
                }
            }

            // Fire the validation tasks in parallel
            var task1 = this.ValidateAppKey(appPrincipal.AppKey);
            var task2 = this.ValidateUserPrincipal(userPrincipal, appPrincipal.AppHandle);
            await Task.WhenAll(task1, task2);

            // Is app key valid?
            if (task1.Result == false)
            {
                string errorMessage = $"Invalid AppKey. AppPrincipal={appPrincipal.ToString()}";
                this.Log.LogException(errorMessage);
            }

            // Is user principal valid?
            if (task2.Result == false)
            {
                string errorMessage = $"Invalid UserPrincipal. UserPrincipal={userPrincipal.ToString()}, AppHandle={appPrincipal.AppHandle}";
                this.Log.LogException(errorMessage);
            }

            return(principals);
        }
        public async Task <IHttpActionResult> PostLinkedAccount([FromBody] PostLinkedAccountRequest request)
        {
            string className  = "MyLinkedAccountsController";
            string methodName = "PostLinkedAccount";
            string logEntry   = $"IdentityProvider = {this.UserPrincipal?.IdentityProvider}";

            this.LogControllerStart(this.log, className, methodName, logEntry);

            // 1. Check that the auth header has no user handle. If it does, it means that the auth filter already found a user handle linked
            // to this credential
            if (this.UserHandle != null)
            {
                this.log.LogError(string.Format("User already has account linked. UserHandle: {0}", this.UserHandle));
                return(this.Conflict(ResponseStrings.LinkedAccountExists));
            }

            // 2. Validate the session token. If token is invalid we return BadRequest (400) and not unauthorized (401).
            List <IPrincipal> principals;

            try
            {
                principals = await this.sessionTokenManager.ValidateToken(request.SessionToken);
            }
            catch (Exception e)
            {
                // Catch exception and log it
                this.log.LogError(string.Format("Session token {0} invalid in PostLinkedAccount", request.SessionToken), e);
                return(this.BadRequest(ResponseStrings.SessionTokenInvalid));
            }

            // Extract app and user principals from session token.
            AppPrincipal  sessionTokenAppPrincipal  = null;
            UserPrincipal sessionTokenUserPrincipal = null;

            foreach (IPrincipal p in principals)
            {
                if (p is AppPrincipal)
                {
                    sessionTokenAppPrincipal = p as AppPrincipal;
                }
                else
                {
                    sessionTokenUserPrincipal = p as UserPrincipal;
                }
            }

            // 3. Check that the app principal extracted from session token matches the one in the auth filter.
            if (sessionTokenAppPrincipal != this.AppPrincipal)
            {
                this.log.LogError($"Session token belongs to app {sessionTokenAppPrincipal.ToString()} whereas the request's token belongs to app {this.AppPrincipal.ToString()}");
                return(this.BadRequest(ResponseStrings.SessionTokenInvalid));
            }

            // 4. Check if the account is linked already. For this we use the user handle from the session token and the identity provider from the Auth header
            var linkedAccountEntity = await this.usersManager.ReadLinkedAccount(sessionTokenUserPrincipal.UserHandle, this.UserPrincipal.IdentityProvider);

            if (linkedAccountEntity != null)
            {
                this.log.LogError($"User already has account linked. UserHandle: {sessionTokenUserPrincipal.UserHandle}, IdentityProvider: {this.UserPrincipal.IdentityProvider}");
                return(this.Conflict(ResponseStrings.LinkedAccountExists));
            }

            // 5. Finally link account
            UserPrincipal linkedAccountUserPrincipal = new UserPrincipal(this.log, sessionTokenUserPrincipal.UserHandle, this.UserPrincipal.IdentityProvider, this.UserPrincipal.IdentityProviderAccountId);

            await this.usersManager.CreateLinkedAccount(ProcessType.Frontend, linkedAccountUserPrincipal);

            logEntry += $", SessionTokenAppHandle = {sessionTokenAppPrincipal?.AppHandle}, SessionTokenUserHandle = {sessionTokenUserPrincipal?.UserHandle}";
            this.LogControllerEnd(this.log, className, methodName, logEntry);
            return(this.NoContent());
        }
 /// <summary>
 /// Generates a claim from an appPrincipal
 /// </summary>
 /// <param name="appPrincipal">appPrincipal input</param>
 /// <returns>claim</returns>
 private Claim GenerateAppClaim(AppPrincipal appPrincipal)
 {
     return(new Claim("iss", appPrincipal.ToString()));
 }