forked from casdoor/casdoor
feat: add Alibaba Cloud ID verification provider (#4645)
This commit is contained in:
1
go.mod
1
go.mod
@@ -7,6 +7,7 @@ require (
|
||||
github.com/NdoleStudio/lemonsqueezy-go v1.2.4
|
||||
github.com/PaddleHQ/paddle-go-sdk v1.0.0
|
||||
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
|
||||
github.com/alibabacloud-go/cloudauth-20190307/v3 v3.9.2
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4
|
||||
github.com/alibabacloud-go/facebody-20191230/v5 v5.1.2
|
||||
github.com/alibabacloud-go/openapi-util v0.1.0
|
||||
|
||||
4
go.sum
4
go.sum
@@ -110,6 +110,8 @@ github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do2
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
|
||||
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
|
||||
github.com/alibabacloud-go/cloudauth-20190307/v3 v3.9.2 h1:y4s0WQ1jrBtOJfXGgsv/83brJvkkHbFdORp0WDyVAuw=
|
||||
github.com/alibabacloud-go/cloudauth-20190307/v3 v3.9.2/go.mod h1:kD75qqMQyjCiz6lssjRzYGTumcli8STLXQstVe6ytxk=
|
||||
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
|
||||
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
|
||||
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
|
||||
@@ -120,6 +122,7 @@ github.com/alibabacloud-go/darabonba-number v1.0.4 h1:aTY1TanasI0A1AYT3Co+PLttFS
|
||||
github.com/alibabacloud-go/darabonba-number v1.0.4/go.mod h1:9NJbJwLCPxHzFwYqnr27G2X8pSTAz0uSQEJsrjr/kqw=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4 h1:IGSZHlOnWwBbLtX5xDplQvZOH0nkrV7Wmq+Fto7JK5w=
|
||||
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4/go.mod h1:Wxis0IBFusdbo44HO6KYYCJR1rRkoh47QQOYWvaheSU=
|
||||
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
|
||||
@@ -163,6 +166,7 @@ github.com/alibabacloud-go/tea-utils v1.3.6 h1:bVjrxHztM8hAs6nOfLWCgxQfAtKb9RgFF
|
||||
github.com/alibabacloud-go/tea-utils v1.3.6/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.0/go.mod h1:U5MTY10WwlquGPS34DOeomUGBB0gXbLueiq5Trwu0C4=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
|
||||
github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
|
||||
github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||
|
||||
111
idv/aliyun.go
Normal file
111
idv/aliyun.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2025 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package idv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
cloudauth "github.com/alibabacloud-go/cloudauth-20190307/v3/client"
|
||||
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
|
||||
"github.com/alibabacloud-go/tea/tea"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultAlibabaCloudEndpoint is the default endpoint for Alibaba Cloud ID verification service
|
||||
DefaultAlibabaCloudEndpoint = "cloudauth.cn-shanghai.aliyuncs.com"
|
||||
)
|
||||
|
||||
type AlibabaCloudIdvProvider struct {
|
||||
ClientId string
|
||||
ClientSecret string
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
func NewAlibabaCloudIdvProvider(clientId string, clientSecret string, endpoint string) *AlibabaCloudIdvProvider {
|
||||
return &AlibabaCloudIdvProvider{
|
||||
ClientId: clientId,
|
||||
ClientSecret: clientSecret,
|
||||
Endpoint: endpoint,
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *AlibabaCloudIdvProvider) VerifyIdentity(idCardType string, idCard string, realName string) (bool, error) {
|
||||
if provider.ClientId == "" || provider.ClientSecret == "" {
|
||||
return false, fmt.Errorf("Alibaba Cloud credentials not configured")
|
||||
}
|
||||
|
||||
if idCard == "" || realName == "" {
|
||||
return false, fmt.Errorf("ID card and real name are required")
|
||||
}
|
||||
|
||||
// Default endpoint if not configured
|
||||
endpoint := provider.Endpoint
|
||||
if endpoint == "" {
|
||||
endpoint = DefaultAlibabaCloudEndpoint
|
||||
}
|
||||
|
||||
// Create client configuration
|
||||
config := &openapi.Config{
|
||||
AccessKeyId: tea.String(provider.ClientId),
|
||||
AccessKeySecret: tea.String(provider.ClientSecret),
|
||||
Endpoint: tea.String(endpoint),
|
||||
}
|
||||
|
||||
// Create Alibaba Cloud Auth client
|
||||
client, err := cloudauth.NewClient(config)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to create Alibaba Cloud client: %v", err)
|
||||
}
|
||||
|
||||
// Prepare verification request using Id2MetaVerify API
|
||||
// This API verifies Chinese ID card number and real name
|
||||
// Reference: https://help.aliyun.com/zh/id-verification/financial-grade-id-verification/server-side-integration-2
|
||||
request := &cloudauth.Id2MetaVerifyRequest{
|
||||
IdentifyNum: tea.String(idCard),
|
||||
UserName: tea.String(realName),
|
||||
ParamType: tea.String("normal"),
|
||||
}
|
||||
|
||||
// Send verification request
|
||||
response, err := client.Id2MetaVerify(request)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to verify identity with Alibaba Cloud: %v", err)
|
||||
}
|
||||
|
||||
// Check response
|
||||
if response == nil || response.Body == nil {
|
||||
return false, fmt.Errorf("empty response from Alibaba Cloud")
|
||||
}
|
||||
|
||||
// Check if the API call was successful
|
||||
if response.Body.Code == nil || *response.Body.Code != "200" {
|
||||
message := "unknown error"
|
||||
if response.Body.Message != nil {
|
||||
message = *response.Body.Message
|
||||
}
|
||||
return false, fmt.Errorf("Alibaba Cloud API error: %s", message)
|
||||
}
|
||||
|
||||
// Check verification result
|
||||
// BizCode "1" means verification passed
|
||||
if response.Body.ResultObject != nil && response.Body.ResultObject.BizCode != nil {
|
||||
if *response.Body.ResultObject.BizCode == "1" {
|
||||
return true, nil
|
||||
}
|
||||
return false, fmt.Errorf("identity verification failed: BizCode=%s", *response.Body.ResultObject.BizCode)
|
||||
}
|
||||
|
||||
return false, fmt.Errorf("identity verification failed: missing result")
|
||||
}
|
||||
@@ -21,7 +21,9 @@ type IdvProvider interface {
|
||||
func GetIdvProvider(typ string, clientId string, clientSecret string, endpoint string) IdvProvider {
|
||||
if typ == "Jumio" {
|
||||
return NewJumioIdvProvider(clientId, clientSecret, endpoint)
|
||||
} else if typ == "Alibaba Cloud" {
|
||||
return NewAlibabaCloudIdvProvider(clientId, clientSecret, endpoint)
|
||||
}
|
||||
// Default to Jumio as it's the only supported provider currently
|
||||
// Default to Jumio for backward compatibility
|
||||
return NewJumioIdvProvider(clientId, clientSecret, endpoint)
|
||||
}
|
||||
|
||||
@@ -341,6 +341,12 @@ class ProviderEditPage extends React.Component {
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
|
||||
}
|
||||
case "ID Verification":
|
||||
if (provider.type === "Alibaba Cloud") {
|
||||
return Setting.getLabel(i18next.t("provider:Access key"), i18next.t("provider:Access key - Tooltip"));
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
|
||||
}
|
||||
default:
|
||||
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
|
||||
}
|
||||
@@ -394,6 +400,12 @@ class ProviderEditPage extends React.Component {
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||
}
|
||||
case "ID Verification":
|
||||
if (provider.type === "Alibaba Cloud") {
|
||||
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip"));
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||
}
|
||||
default:
|
||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||
}
|
||||
@@ -711,12 +723,16 @@ class ProviderEditPage extends React.Component {
|
||||
this.updateProviderField("type", "RADIUS");
|
||||
this.updateProviderField("host", "");
|
||||
this.updateProviderField("port", 1812);
|
||||
} else if (value === "ID Verification") {
|
||||
this.updateProviderField("type", "Jumio");
|
||||
this.updateProviderField("endpoint", "");
|
||||
}
|
||||
})}>
|
||||
{
|
||||
[
|
||||
{id: "Captcha", name: "Captcha"},
|
||||
{id: "Email", name: "Email"},
|
||||
{id: "ID Verification", name: "ID Verification"},
|
||||
{id: "MFA", name: "MFA"},
|
||||
{id: "Notification", name: "Notification"},
|
||||
{id: "OAuth", name: "OAuth"},
|
||||
@@ -1071,7 +1087,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
{["Face ID", "Storage"].includes(this.state.provider.category) || ["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "CUCloud"].includes(this.state.provider.type) ? (
|
||||
{["Face ID", "Storage", "ID Verification"].includes(this.state.provider.category) || ["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "CUCloud"].includes(this.state.provider.type) ? (
|
||||
<div>
|
||||
{["Local File System", "CUCloud"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
@@ -1085,7 +1101,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology", "Casdoor", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
{this.state.provider.category === "ID Verification" || ["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology", "Casdoor", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
|
||||
@@ -1097,7 +1113,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "Local File System", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
{this.state.provider.category === "ID Verification" || ["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "Local File System", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{["Casdoor"].includes(this.state.provider.type) ?
|
||||
@@ -1111,7 +1127,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
{this.state.provider.category === "ID Verification" || ["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Path prefix"), i18next.t("provider:Path prefix - Tooltip"))} :
|
||||
@@ -1123,7 +1139,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "Synology", "Casdoor", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
{this.state.provider.category === "ID Verification" || ["Custom HTTP SMS", "Custom HTTP Email", "SendGrid", "Synology", "Casdoor", "CUCloud", "Alibaba Cloud Facebody"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||
|
||||
@@ -1387,6 +1387,11 @@ export function getProviderTypeOptions(category) {
|
||||
return ([
|
||||
{id: "RADIUS", name: "RADIUS"},
|
||||
]);
|
||||
} else if (category === "ID Verification") {
|
||||
return ([
|
||||
{id: "Jumio", name: "Jumio"},
|
||||
{id: "Alibaba Cloud", name: "Alibaba Cloud"},
|
||||
]);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user