Files
tnb.server/common/Tnb.CollectiveOAuth/Request/AuthRequests/LinkedinAuthRequest.cs
2023-03-13 15:00:34 +08:00

234 lines
7.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using JNPF.Extras.CollectiveOAuth.Cache;
using JNPF.Extras.CollectiveOAuth.Config;
using JNPF.Extras.CollectiveOAuth.Models;
using JNPF.Extras.CollectiveOAuth.Utils;
using JNPF.Extras.CollectiveOAuth.Enums;
namespace JNPF.Extras.CollectiveOAuth.Request;
public class LinkedInAuthRequest : DefaultAuthRequest
{
public LinkedInAuthRequest(ClientConfig config) : base(config, new LinkedInAuthSource())
{
}
public LinkedInAuthRequest(ClientConfig config, IAuthStateCache authStateCache)
: base(config, new LinkedInAuthSource(), authStateCache)
{
}
protected override AuthToken getAccessToken(AuthCallback authCallback)
{
return this.getToken(accessTokenUrl(authCallback.code));
}
protected override AuthUser getUserInfo(AuthToken authToken)
{
var accessToken = authToken.accessToken;
var reqParams = new Dictionary<string, object>
{
{ "Host", "api.linkedin.com" },
{ "Connection", "Keep-Alive" },
{ "Authorization", "Bearer " + accessToken }
};
var response = HttpUtils.RequestGet(userInfoUrl(authToken), reqParams);
var userObj = response.parseObject();
this.checkResponse(userObj);
string userName = getUserName(userObj);
// 获取用户头像
string avatar = this.getAvatar(userObj);
// 获取用户邮箱地址
string email = this.getUserEmail(accessToken);
var authUser = new AuthUser
{
uuid = userObj.getString("id"),
username = userName,
nickname = userName,
avatar = avatar,
email = email,
gender = AuthUserGender.UNKNOWN,
token = authToken,
source = source.getName(),
originalUser = userObj,
originalUserStr = response
};
return authUser;
}
/**
* 获取用户的真实名
*
* @param userInfoObject 用户json对象
* @return 用户名
*/
private string getUserName(Dictionary<string, object> userInfoObject)
{
string firstName, lastName;
// 获取firstName
if (userInfoObject.ContainsKey("localizedFirstName"))
{
firstName = userInfoObject.getString("localizedFirstName");
}
else
{
firstName = getUserName(userInfoObject, "firstName");
}
// 获取lastName
if (userInfoObject.ContainsKey("localizedLastName"))
{
lastName = userInfoObject.getString("localizedLastName");
}
else
{
lastName = getUserName(userInfoObject, "lastName");
}
return firstName + " " + lastName;
}
/**
* 获取用户的头像
*
* @param userInfoObject 用户json对象
* @return 用户的头像地址
*/
private string getAvatar(Dictionary<string, object> userInfoObject)
{
string avatar = null;
var profilePictureObject = userInfoObject.getJSONObject("profilePicture");
if (profilePictureObject.ContainsKey("displayImage~"))
{
var displayImageElements = profilePictureObject.getJSONObject("displayImage~")
.getJSONArray("elements");
if (null != displayImageElements && displayImageElements.Count > 0)
{
var largestImageObj = displayImageElements[displayImageElements.Count - 1];
avatar = largestImageObj.getJSONArray("identifiers")[0].getString("identifier");
}
}
return avatar;
}
/**
* 获取用户的email
*
* @param accessToken 用户授权后返回的token
* @return 用户的邮箱地址
*/
private string getUserEmail(string accessToken)
{
string email = null;
var reqParams = new Dictionary<string, object>();
reqParams.Add("Host", "api.linkedin.com");
reqParams.Add("Connection", "Keep-Alive");
reqParams.Add("Authorization", "Bearer " + accessToken);
var emailResponse = HttpUtils.RequestGet("https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))", reqParams);
var emailObj = emailResponse.parseObject();
this.checkResponse(emailObj);
var listObject = emailObj.getJSONArray("elements");
if (listObject != null && listObject.Count > 0)
{
email = listObject[listObject.Count - 1].getJSONObject("handle~").getString("emailAddress");
}
return email;
}
private string getUserName(Dictionary<string, object> userInfoObject, string nameKey)
{
string firstName;
var firstNameObj = userInfoObject.getJSONObject(nameKey);
var localizedObj = firstNameObj.getJSONObject("localized");
var preferredLocaleObj = firstNameObj.getJSONObject("preferredLocale");
firstName = localizedObj.getString(preferredLocaleObj.getString("language") + "_" + preferredLocaleObj.getString("country"));
return firstName;
}
public override AuthResponse refresh(AuthToken oldToken)
{
string refreshToken = oldToken.refreshToken;
if (refreshToken.IsNullOrWhiteSpace())
{
throw new Exception(AuthResponseStatus.REQUIRED_REFRESH_TOKEN.GetDesc());
}
string refreshTokenUrl = this.refreshTokenUrl(refreshToken);
return new AuthResponse(AuthResponseStatus.SUCCESS.GetCode(), AuthResponseStatus.SUCCESS.GetDesc(), this.getToken(refreshTokenUrl));
}
/**
* 获取token适用于获取access_token和刷新token
*
* @param accessTokenUrl 实际请求token的地址
* @return token对象
*/
private AuthToken getToken(string accessTokenUrl)
{
var reqParams = new Dictionary<string, object>();
reqParams.Add("Host", "www.linkedin.com");
reqParams.Add("Content-Type", "application/x-www-form-urlencoded");
string response = HttpUtils.RequestPost(accessTokenUrl, null, reqParams);
string accessTokenStr = response;
var accessTokenObject = accessTokenStr.parseObject();
this.checkResponse(accessTokenObject);
var authToken = new AuthToken();
authToken.accessToken = accessTokenObject.getString("access_token");
authToken.refreshToken = accessTokenObject.getString("refresh_token");
authToken.expireIn = accessTokenObject.getInt32("expire_in");
return authToken;
}
/**
* 返回带{@code state}参数的授权url授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数可以防止csrf
* @return 返回授权地址
* @since 1.9.3
*/
public override string authorize(string state)
{
return UrlBuilder.fromBaseUrl(source.authorize())
.queryParam("response_type", "code")
.queryParam("client_id", config.clientId)
.queryParam("redirect_uri", config.redirectUri)
.queryParam("scope", config.scope.IsNullOrWhiteSpace() ? "r_liteprofile%20r_emailaddress%20w_member_social" : config.scope)
.queryParam("state", getRealState(state))
.build();
}
/**
* 返回获取userInfo的url
*
* @param authToken 用户授权后的token
* @return 返回获取userInfo的url
*/
protected override string userInfoUrl(AuthToken authToken)
{
return UrlBuilder.fromBaseUrl(source.userInfo())
.queryParam("projection", "(id,firstName,lastName,profilePicture(displayImage~:playableStreams))")
.build();
}
/**
* 校验请求结果
*
* @param response 请求结果
* @return 如果请求结果正常则返回Exception
*/
private void checkResponse(Dictionary<string, object> dic)
{
if (dic.ContainsKey("error"))
{
throw new Exception($"{dic.getString("error_description")}");
}
}
}