Single Sign-On
Third-Party Integration
Understanding OAuth Protocol
OAuth protocol provides a secure and standardized method for authorizing user resources. Unlike traditional authorization methods, OAuth enables third-party applications to access user resources without exposing the user's credentials (such as username and password). This approach maintains security by preventing third parties from handling sensitive authentication information.
OAuth, which stands for Open Authorization, is not a standardized implementation but rather a framework that backend developers implement according to their specific requirements and the OAuth specifications.
The OAuth2.0 flow typically involves these steps:
- The third-party application requests user authorization
- The user approves the authorization request
- Using the authorization obtained, the third-party requests an access token from the authorization server
- The authorization server validates the authorization and issues an access token
- The third-party uses the access token to request resources from the resource server
- The resource server verifies the token with the authorization server and provides the requested resources
OAuth2.0 Problem Solving
Authentication systems fundamentally address trust issues between parties. In OAuth2.0, three primary entities are involved:
- Resource Owner: The user who possesses the resources
- Client: The application requesting access to resources
- Resource Server: The server hosting the protected resources
- Authorization Server: The server issuing access tokens after validating credentials
The OAuth2.0 framework establishes trust relationships between these entities by:
- Ensuring the service provider can verify the user's identity through credentials
- Verifying the client's identity using provided credentials
- Allowing users to grant limited access to their resources without sharing passwords
SpringBoot Integration with OAuth2.0
Implementation details for SpringBoot integration will be added in a future update.
DingTalk QR Code Login Integration
Setting Up the QR Code Login Page
To implement DingTalk QR code login, follow these steps:
- Create an application in the DingTalk developer console
- Configure the callback domain in your application settings
- Construct the QR code login page using one of two methods
Method 1: Using DingTalk's QR Code Login Page
Redirect users to the following URL when they initiate DingTalk login:
https://oapi.dingtalk.com/connect/qrconnect?appid=APP_ID&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=REDIRECT_URI
After the user scans the QR code and confirms, they will be redirected to your specified redirect_uri with the authorization code and state parameters appended to the URL.
Important: The redirect_uri domain must match the callback domain configured in your DingTalk application settings.
Method 2: Embedding the QR Code in Your Own Page
To embed the QR code directly in your webpage:
- Include the DingTalk login JavaScript file:
<script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
- Initialize the DDLogin object where you want the QR code to appear:
var obj = DDLogin({
id: "login_container",
goto: "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=APP_ID&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=REDIRECT_URI",
style: "border:none;background-color:#FFFFFF;",
width: "365",
height: "400"
});
- Handle the loginTmpCode returned after scanning:
var handleMessage = function(event) {
var origin = event.origin;
if(origin == "https://login.dingtalk.com") {
var loginTmpCode = event.data;
// Use loginTmpCode to construct the authorization URL
window.location.href = "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=APP_ID&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=REDIRECT_URI&loginTmpCode=" + loginTmpCode;
}
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', handleMessage, false);
} else if (typeof window.attachEvent != 'undefined') {
window.attachEvent('onmessage', handleMessage);
}
Backend Implementation
After receiving the authorization code, your backend should:
- Call the DingTalk API to get user information using the code
- Retrieve the user's unionid
- Get the userid using the unionid
- Fetch detailed user information using the userid
- Create or update the user in your system and generate an authentication token
Example Frontend Code (Vue.js)
<template>
<div class="login-container">
<div class="login-weaper" v-if="loginif">
<div class="login-left">
<p class="title">System Login</p>
</div>
<div class="login-border">
<div class="right-top" @click="toggleLoginMethod" v-if="showQRCodeOption">
<img src="../assets/qr-icon.png" v-if="!useQRCode"/>
<img src="../assets/qr-active.png" v-else/>
</div>
<div id="qr_container" v-show="useQRCode"></div>
<div class="login-main" v-show="!useQRCode">
<!-- Username/password form fields -->
</div>
</div>
</div>
<div class="login_box" v-else>
<span class="login_text">Authenticating...</span>
<span v-loading="loading" class="loading"></span>
</div>
</div>
</template>
<script>
import { fetchDingTalkConfig } from '@/api/auth'
import { authenticateWithCode } from '@/api/login'
export default {
name: 'Login',
data() {
return {
loading: false,
loginif: true,
useQRCode: true,
showQRCodeOption: true,
dingTalkConfig: {
appKey: '',
agentId: '',
corpId: '',
accessKey: ''
}
}
},
mounted() {
if (this.$route.query && this.$route.query.code) {
this.handleCodeLogin(this.$route.query.code)
}
this.loadDingTalkConfig()
},
methods: {
loadDingTalkConfig() {
fetchDingTalkConfig().then(res => {
this.dingTalkConfig = res.data
if (this.dingTalkConfig.accessKey) {
this.initDingTalkQR()
}
})
},
initDingTalkQR() {
const redirectUri = encodeURIComponent('http://' + location.host + '/login')
const authUrl = `https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=${this.dingTalkConfig.accessKey}&response_type=code&scope=snsapi_login&state=STATE&redirect_uri=${redirectUri}`
const qrContainer = document.createElement('div')
qrContainer.id = 'login_code'
document.getElementById('qr_container').appendChild(qrContainer)
const obj = DDLogin({
id: 'login_code',
goto: encodeURIComponent(authUrl),
style: 'border:none;margin: 0 auto; background-color:inherit;',
width: '500',
height: '300'
})
const handleMessage = (event) => {
if (event.origin === 'https://login.dingtalk.com') {
const loginTmpCode = event.data
window.location.href = `${authUrl}&loginTmpCode=${loginTmpCode}`
}
}
if (typeof window.addEventListener !== 'undefined') {
window.addEventListener('message', handleMessage, false)
} else if (typeof window.attachEvent !== 'undefined') {
window.attachEvent('onmessage', handleMessage)
}
},
handleCodeLogin(code) {
this.loginif = false
authenticateWithCode(code).then(() => {
this.$router.push({ path: '/' })
})
},
toggleLoginMethod() {
this.useQRCode = !this.useQRCode
}
}
}
</script>
Example Backend Code
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@ApiOperation("DingTalk QR code authentication")
@PostMapping("/dingtalk")
public ResponseEntity<?> dingTalkAuth(@RequestParam String code) {
try {
DingTalkUserInfo userInfo = DingTalkService.getUserInfo(code);
String token = AuthenticationService.authenticate(userInfo);
return ResponseEntity.ok()
.body(Map.of(
"success", true,
"token", token,
"user", userInfo
));
} catch (ApiException e) {
return ResponseEntity.badRequest()
.body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
}
DingTalk Service Implementation
<n>@Service
public class DingTalkService {
private String getAppKey() {
return "YOUR_APP_KEY";
}
private String getAppSecret() {
return "YOUR_APP_SECRET";
}
public String getAccessToken() throws ApiException {
DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
OapiGettokenRequest request = new OapiGettokenRequest();
request.setAppkey(getAppKey());
request.setAppsecret(getAppSecret());
request.setHttpMethod("GET");
OapiGettokenResponse response = client.execute(request);
return response.getAccessToken();
}
public DingTalkUserInfo getUserInfo(String authCode) throws ApiException {
String accessToken = getAccessToken();
String appKey = getAppKey();
String appSecret = getAppSecret();
// Get user info using temporary authorization code
DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/sns/getuserinfo_bycode");
OapiSnsGetuserinfoBycodeRequest request = new OapiSnsGetuserinfoBycodeRequest();
request.setTmpAuthCode(authCode);
OapiSnsGetuserinfoBycodeResponse response = client.execute(request, appKey, appSecret);
// Get userid using unionid
String unionid = response.getUserInfo().getUnionid();
DingTalkClient client2 = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/user/getbyunionid");
OapiUserGetbyunionidRequest request2 = new OapiUserGetbyunionidRequest();
request2.setUnionid(unionid);
OapiUserGetbyunionidResponse response2 = client2.execute(request2, accessToken);
// Get detailed user information
String userid = response2.getResult().getUserid();
DingTalkClient client3 = new DefaultDingTalkClient("https://oapi.dingtalk.com/user/get");
OapiUserGetRequest request3 = new OapiUserGetRequest();
request3.setUserid(userid);
OapiUserGetResponse response3 = client3.execute(request3, accessToken);
return new DingTalkUserInfo(response3);
}
}
Additional Third-Party Login Integrations
Similar OAuth2.0 integration patterns can be implemented for other platforms such as:
- WeChat QR Code Login
- WeCom (Enterprise WeChat) Login
- Feishu (Lark) Login
- QQ Login
- Alipay Login
- Gitee Login
Each platform has its specific API endpoints and authentication flows, but the fundamental OAuth2.0 pattern remains consistent across implementations.