@typekcz-nocobase-plugins/plugin-oidc-plus 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.js +1 -3
- package/dist/externalVersion.js +9 -9
- package/package.json +10 -10
- package/src/client/OIDCButton.tsx +70 -0
- package/src/client/Options.tsx +359 -0
- package/src/client/index.tsx +19 -0
- package/src/client/locale/index.ts +18 -0
- package/src/constants.ts +7 -0
- package/src/index.ts +2 -0
- package/src/locale/en-US.json +40 -0
- package/src/locale/es-ES.json +25 -0
- package/src/locale/fr-FR.json +21 -0
- package/src/locale/ko_KR.json +28 -0
- package/src/locale/pt-BR.json +21 -0
- package/src/locale/zh-CN.json +28 -0
- package/src/server/__tests__/oidc.test.ts +283 -0
- package/src/server/actions/getAuthUrl.ts +25 -0
- package/src/server/actions/redirect.ts +32 -0
- package/src/server/index.ts +1 -0
- package/src/server/oidc-auth.ts +169 -0
- package/src/server/plugin.ts +63 -0
- package/src/swagger/index.ts +157 -0
- package/dist/node_modules/nanoid/LICENSE +0 -20
- package/dist/node_modules/nanoid/async/index.browser.cjs +0 -34
- package/dist/node_modules/nanoid/async/index.browser.js +0 -34
- package/dist/node_modules/nanoid/async/index.cjs +0 -35
- package/dist/node_modules/nanoid/async/index.d.ts +0 -56
- package/dist/node_modules/nanoid/async/index.js +0 -35
- package/dist/node_modules/nanoid/async/index.native.js +0 -26
- package/dist/node_modules/nanoid/async/package.json +0 -12
- package/dist/node_modules/nanoid/bin/nanoid.cjs +0 -55
- package/dist/node_modules/nanoid/index.browser.cjs +0 -34
- package/dist/node_modules/nanoid/index.browser.js +0 -34
- package/dist/node_modules/nanoid/index.cjs +0 -1
- package/dist/node_modules/nanoid/index.d.ts +0 -91
- package/dist/node_modules/nanoid/index.js +0 -45
- package/dist/node_modules/nanoid/nanoid.js +0 -1
- package/dist/node_modules/nanoid/non-secure/index.cjs +0 -21
- package/dist/node_modules/nanoid/non-secure/index.d.ts +0 -33
- package/dist/node_modules/nanoid/non-secure/index.js +0 -21
- package/dist/node_modules/nanoid/non-secure/package.json +0 -6
- package/dist/node_modules/nanoid/package.json +0 -1
- package/dist/node_modules/nanoid/url-alphabet/index.cjs +0 -3
- package/dist/node_modules/nanoid/url-alphabet/index.js +0 -3
- package/dist/node_modules/nanoid/url-alphabet/package.json +0 -6
- package/dist/node_modules/openid-client/lib/client.js +0 -1849
- package/dist/node_modules/openid-client/lib/device_flow_handle.js +0 -125
- package/dist/node_modules/openid-client/lib/errors.js +0 -55
- package/dist/node_modules/openid-client/lib/helpers/assert.js +0 -24
- package/dist/node_modules/openid-client/lib/helpers/base64url.js +0 -13
- package/dist/node_modules/openid-client/lib/helpers/client.js +0 -211
- package/dist/node_modules/openid-client/lib/helpers/consts.js +0 -7
- package/dist/node_modules/openid-client/lib/helpers/decode_jwt.js +0 -27
- package/dist/node_modules/openid-client/lib/helpers/deep_clone.js +0 -1
- package/dist/node_modules/openid-client/lib/helpers/defaults.js +0 -27
- package/dist/node_modules/openid-client/lib/helpers/generators.js +0 -14
- package/dist/node_modules/openid-client/lib/helpers/is_key_object.js +0 -4
- package/dist/node_modules/openid-client/lib/helpers/is_plain_object.js +0 -1
- package/dist/node_modules/openid-client/lib/helpers/issuer.js +0 -111
- package/dist/node_modules/openid-client/lib/helpers/keystore.js +0 -298
- package/dist/node_modules/openid-client/lib/helpers/merge.js +0 -24
- package/dist/node_modules/openid-client/lib/helpers/pick.js +0 -9
- package/dist/node_modules/openid-client/lib/helpers/process_response.js +0 -71
- package/dist/node_modules/openid-client/lib/helpers/request.js +0 -200
- package/dist/node_modules/openid-client/lib/helpers/unix_timestamp.js +0 -1
- package/dist/node_modules/openid-client/lib/helpers/weak_cache.js +0 -1
- package/dist/node_modules/openid-client/lib/helpers/webfinger_normalize.js +0 -71
- package/dist/node_modules/openid-client/lib/helpers/www_authenticate_parser.js +0 -14
- package/dist/node_modules/openid-client/lib/index.js +0 -1
- package/dist/node_modules/openid-client/lib/issuer.js +0 -191
- package/dist/node_modules/openid-client/lib/issuer_registry.js +0 -3
- package/dist/node_modules/openid-client/lib/passport_strategy.js +0 -205
- package/dist/node_modules/openid-client/lib/token_set.js +0 -35
- package/dist/node_modules/openid-client/package.json +0 -1
- package/dist/node_modules/openid-client/types/index.d.ts +0 -622
package/dist/client/index.js
CHANGED
|
@@ -7,6 +7,4 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
(function(o,n){typeof exports=="object"&&typeof module!="undefined"?n(exports,require("@nocobase/client"),require("@nocobase/plugin-auth/client"),require("react/jsx-runtime"),require("@ant-design/icons"),require("antd"),require("react"),require("react-i18next"),require("react-router-dom"),require("@formily/antd-v5"),require("@formily/react")):typeof define=="function"&&define.amd?define(["exports","@nocobase/client","@nocobase/plugin-auth/client","react/jsx-runtime","@ant-design/icons","antd","react","react-i18next","react-router-dom","@formily/antd-v5","@formily/react"],n):(o=typeof globalThis!="undefined"?globalThis:o||self,n(o["@typekcz-nocobase-plugins/plugin-oidc-plus"]={},o["@nocobase/client"],o["@nocobase/plugin-auth"],o.jsxRuntime,o["@ant-design/icons"],o.antd,o.react,o["react-i18next"],o["react-router-dom"],o["@formily/antd-v5"],o["@formily/react"]))})(this,function(o,n,h,s,v,u,b,I,d,y,U){"use strict";var w=(o,n,h)=>new Promise((s,v)=>{var u=d=>{try{I(h.next(d))}catch(y){v(y)}},b=d=>{try{I(h.throw(d))}catch(y){v(y)}},I=d=>d.done?s(d.value):Promise.resolve(d.value).then(u,b);I((h=h.apply(o,n)).next())});const j="OIDC+",T="tnp_oidc_plus_logout",P="oidc";function c(e){return n.i18n.t(e,{ns:P})}function C(){return I.useTranslation(P)}/*! js-cookie v3.0.5 | MIT */function S(e){for(var a=1;a<arguments.length;a++){var l=arguments[a];for(var m in l)e[m]=l[m]}return e}var q={read:function(e){return e[0]==='"'&&(e=e.slice(1,-1)),e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};function F(e,a){function l(t,x,r){if(typeof document!="undefined"){r=S({},a,r),typeof r.expires=="number"&&(r.expires=new Date(Date.now()+r.expires*864e5)),r.expires&&(r.expires=r.expires.toUTCString()),t=encodeURIComponent(t).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var i="";for(var p in r)r[p]&&(i+="; "+p,r[p]!==!0&&(i+="="+r[p].split(";")[0]));return document.cookie=t+"="+e.write(x,t)+i}}function m(t){if(!(typeof document=="undefined"||arguments.length&&!t)){for(var x=document.cookie?document.cookie.split("; "):[],r={},i=0;i<x.length;i++){var p=x[i].split("="),f=p.slice(1).join("=");try{var g=decodeURIComponent(p[0]);if(r[g]=e.read(f,g),t===g)break}catch(D){}}return t?r[t]:r}}return Object.create({set:l,get:m,remove:function(t,x){l(t,"",S({},x,{expires:-1}))},withAttributes:function(t){return F(this.converter,S({},this.attributes,t))},withConverter:function(t){return F(S({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(a)},converter:{value:Object.freeze(e)}})}var k=F(q,{path:"/"});const E=({authenticator:e})=>{const{t:a}=C(),l=n.useAPIClient(),m=d.useLocation(),t=new URLSearchParams(m.search),x=t.get("redirect"),r=()=>w(this,null,function*(){var f;const i=yield l.request({method:"post",url:"oidc:getAuthUrl",headers:{"X-Authenticator":e.name},data:{redirect:x}}),p=(f=i==null?void 0:i.data)==null?void 0:f.data;window.location.replace(p)});return b.useEffect(()=>{const i=k.get(T);if(i){const g=new URL(i);g.searchParams.set("post_logout_redirect_uri",window.location.href),console.log(T,{domain:window.location.hostname}),k.remove(T,{domain:window.location.hostname}),window.location.href=g.href}const p=t.get("authenticator"),f=t.get("error");if(p===e.name&&f){u.message.error(a(f));return}}),s.jsx(u.Space,{direction:"vertical",className:n.css`
|
|
11
|
-
display: flex;
|
|
12
|
-
`,children:s.jsx(u.Button,{shape:"round",block:!0,icon:s.jsx(v.LoginOutlined,{}),onClick:r,children:a(e.title)})})},O={type:"object",properties:{public:{type:"object",properties:{autoSignup:{"x-decorator":"FormItem",type:"boolean",title:'{{t("Sign up automatically when the user does not exist")}}',"x-component":"Checkbox",default:!0}}},oidc:{type:"object",properties:{collapse:{type:"void","x-component":"FormTab",properties:{basic:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:c("Basic configuration")},properties:{issuer:{type:"string",title:'{{t("Issuer")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientId:{type:"string",title:'{{t("Client ID")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientSecret:{type:"string",title:'{{t("Client Secret")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},scope:{type:"string",title:'{{t("scope")}}',"x-component":"Input","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Default: openid profile email")}}'}},idTokenSignedResponseAlg:{type:"string",title:'{{t("id_token signed response algorithm")}}',"x-component":"Select","x-decorator":"FormItem",enum:[{label:"HS256",value:"HS256"},{label:"HS384",value:"HS384"},{label:"HS512",value:"HS512"},{label:"RS256",value:"RS256"},{label:"RS384",value:"RS384"},{label:"RS512",value:"RS512"},{label:"ES256",value:"ES256"},{label:"ES384",value:"ES384"},{label:"ES512",value:"ES512"},{label:"PS256",value:"PS256"},{label:"PS384",value:"PS384"},{label:"PS512",value:"PS512"}]}}},mapping:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:c("Field mapping")},properties:{fieldMap:{title:'{{t("Field Map")}}',type:"array","x-decorator":"FormItem","x-component":"ArrayItems",items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{source:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("source")}}'}},target:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("target")}}'}},remove:{type:"void","x-decorator":"FormItem","x-component":"ArrayItems.Remove"}}}}},properties:{add:{type:"void",title:"Add","x-component":"ArrayItems.Addition"}}},userBindField:{type:"string",title:'{{t("Use this field to bind the user")}}',"x-component":"Select","x-decorator":"FormItem",default:"email",enum:[{label:c("Email"),value:"email"},{label:c("Username"),value:"username"}],required:!0}}},advanced:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:c("Advanced configuration")},properties:{logout:{type:"boolean",title:'{{t("RP-initiated logout")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Performs logout on the issuer (uses end_session_endpoint in the issuer configuration)")}}'}},http:{type:"boolean",title:'{{t("HTTP")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Check if NocoBase is running on HTTP protocol")}}'}},port:{type:"number",title:'{{t("Port")}}',"x-component":"InputNumber","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("The port number of the NocoBase service if it is not 80 or 443")}}'},"x-component-props":{style:{width:"15%",minWidth:"100px"}}},stateToken:{type:"string",title:'{{t("State token")}}',"x-component":"Input","x-decorator":"FormItem",description:c("The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation.")},exchangeBodyKeys:{type:"array",title:'{{t("Pass parameters in the authorization code grant exchange")}}',"x-decorator":"FormItem","x-component":"ArrayItems",default:[{paramName:"",optionsKey:"clientId"},{paramName:"",optionsKey:"clientSecret"}],items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{enabled:{type:"boolean","x-decorator":"FormItem","x-component":"Checkbox"},optionsKey:{type:"string","x-decorator":"FormItem","x-decorator-props":{style:{width:"100px"}},"x-component":"Select","x-read-pretty":!0,enum:[{label:c("Client ID"),value:"clientId"},{label:c("Client Secret"),value:"clientSecret"}]},paramName:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("Parameter name")}}'}}}}}}},userInfoMethod:{type:"string",title:'{{t("Method to call the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"GET",enum:[{label:"GET",value:"GET"},{label:"POST",value:"POST"}],"x-reactions":[{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "query"}}',fulfill:{state:{value:"GET"}}},{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "body"}}',fulfill:{state:{value:"POST"}}}]},accessTokenVia:{type:"string",title:'{{t("Where to put the access token when calling the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"header",enum:[{label:c("Header"),value:"header"},{label:c("Body (Use with POST method)"),value:"body"},{label:c("Query parameters (Use with GET method)"),value:"query"}]}}}}}}},usage:{type:"void","x-component":"Usage"}}},R=U.observer(()=>{const{t:e}=C(),a=n.useApp(),l=b.useMemo(()=>a.getApiUrl("oidc:redirect"),[a]),m=t=>{navigator.clipboard.writeText(t),u.message.success(e("Copied"))};return s.jsx(u.Card,{title:e("Usage"),type:"inner",children:s.jsx(n.FormItem,{label:e("Redirect URL"),children:s.jsx(n.Input,{value:l,disabled:!0,addonBefore:s.jsx(v.CopyOutlined,{onClick:()=>m(l)})})})})},{displayName:"Usage"}),B=()=>{const{t:e}=C();return s.jsx(n.SchemaComponent,{scope:{t:e},components:{Usage:R,ArrayItems:y.ArrayItems,Space:u.Space,FormTab:y.FormTab},schema:O})};class A extends n.Plugin{load(){return w(this,null,function*(){this.app.pm.get(h).registerType(j,{components:{SignInButton:E,AdminSettingsForm:B}})})}}o.PluginOIDCClient=A,o.default=A,Object.defineProperties(o,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
|
10
|
+
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react-i18next"),require("@nocobase/plugin-auth/client"),require("@nocobase/client"),require("react-router-dom"),require("react"),require("@formily/antd-v5"),require("@formily/react"),require("@ant-design/icons"),require("antd")):"function"==typeof define&&define.amd?define("@typekcz-nocobase-plugins/plugin-oidc-plus",["react-i18next","@nocobase/plugin-auth/client","@nocobase/client","react-router-dom","react","@formily/antd-v5","@formily/react","@ant-design/icons","antd"],t):"object"==typeof exports?exports["@typekcz-nocobase-plugins/plugin-oidc-plus"]=t(require("react-i18next"),require("@nocobase/plugin-auth/client"),require("@nocobase/client"),require("react-router-dom"),require("react"),require("@formily/antd-v5"),require("@formily/react"),require("@ant-design/icons"),require("antd")):e["@typekcz-nocobase-plugins/plugin-oidc-plus"]=t(e["react-i18next"],e["@nocobase/plugin-auth/client"],e["@nocobase/client"],e["react-router-dom"],e.react,e["@formily/antd-v5"],e["@formily/react"],e["@ant-design/icons"],e.antd)}(self,function(e,t,r,o,n,i,a,c,u){return function(){"use strict";var l={482:function(e){e.exports=c},632:function(e){e.exports=i},505:function(e){e.exports=a},772:function(e){e.exports=r},689:function(e){e.exports=t},721:function(e){e.exports=u},156:function(e){e.exports=n},238:function(t){t.exports=e},128:function(e){e.exports=o}},p={};function s(e){var t=p[e];if(void 0!==t)return t.exports;var r=p[e]={exports:{}};return l[e](r,r.exports,s),r.exports}s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,{a:t}),t},s.d=function(e,t){for(var r in t)s.o(t,r)&&!s.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var d={};return!function(){s.r(d),s.d(d,{default:function(){return R},PluginOIDCClient:function(){return j}});var e=s(772),t=s(689),r=s.n(t),o="tnp_oidc_plus_logout",n=s(482),i=s(721),a=s(156),c=s.n(a),u=s(238),l="oidc";function p(t){return e.i18n.t(t,{ns:l})}function f(){return(0,u.useTranslation)(l)}var m=s(128);function y(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var o in r)e[o]=r[o]}return e}var b=function e(t,r){function o(e,o,n){if("undefined"!=typeof document){"number"==typeof(n=y({},r,n)).expires&&(n.expires=new Date(Date.now()+864e5*n.expires)),n.expires&&(n.expires=n.expires.toUTCString()),e=encodeURIComponent(e).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var i="";for(var a in n)n[a]&&(i+="; "+a,!0!==n[a]&&(i+="="+n[a].split(";")[0]));return document.cookie=e+"="+t.write(o,e)+i}}return Object.create({set:o,get:function(e){if("undefined"!=typeof document&&(!arguments.length||e)){for(var r=document.cookie?document.cookie.split("; "):[],o={},n=0;n<r.length;n++){var i=r[n].split("="),a=i.slice(1).join("=");try{var c=decodeURIComponent(i[0]);if(o[c]=t.read(a,c),e===c)break}catch(e){}}return e?o[e]:o}},remove:function(e,t){o(e,"",y({},t,{expires:-1}))},withAttributes:function(t){return e(this.converter,y({},this.attributes,t))},withConverter:function(t){return e(y({},this.converter,t),this.attributes)}},{attributes:{value:Object.freeze(r)},converter:{value:Object.freeze(t)}})}({read:function(e){return'"'===e[0]&&(e=e.slice(1,-1)),e.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(e){return encodeURIComponent(e).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}},{path:"/"});function v(e,t,r,o,n,i,a){try{var c=e[i](a),u=c.value}catch(e){r(e);return}c.done?t(u):Promise.resolve(u).then(o,n)}function h(){var e,t,r=(e=["\n display: flex;\n "],t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}})));return h=function(){return r},r}var x=function(t){var r,u,l=t.authenticator,p=f().t,s=(0,e.useAPIClient)(),d=new URLSearchParams((0,m.useLocation)().search),y=d.get("redirect"),x=(r=function(){var e,t,r;return function(e,t){var r,o,n,i,a={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){var u=[i,c];if(r)throw TypeError("Generator is already executing.");for(;a;)try{if(r=1,o&&(n=2&u[0]?o.return:u[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,u[1])).done)return n;switch(o=0,n&&(u=[2&u[0],n.value]),u[0]){case 0:case 1:n=u;break;case 4:return a.label++,{value:u[1],done:!1};case 5:a.label++,o=u[1],u=[0];continue;case 7:u=a.ops.pop(),a.trys.pop();continue;default:if(!(n=(n=a.trys).length>0&&n[n.length-1])&&(6===u[0]||2===u[0])){a=0;continue}if(3===u[0]&&(!n||u[1]>n[0]&&u[1]<n[3])){a.label=u[1];break}if(6===u[0]&&a.label<n[1]){a.label=n[1],n=u;break}if(n&&a.label<n[2]){a.label=n[2],a.ops.push(u);break}n[2]&&a.ops.pop(),a.trys.pop();continue}u=t.call(e,a)}catch(e){u=[6,e],o=0}finally{r=n=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}}}(this,function(o){switch(o.label){case 0:return[4,s.request({method:"post",url:"oidc:getAuthUrl",headers:{"X-Authenticator":l.name},data:{redirect:y}})];case 1:return r=null==(t=o.sent())||null==(e=t.data)?void 0:e.data,window.location.replace(r),[2]}})},u=function(){var e=this,t=arguments;return new Promise(function(o,n){var i=r.apply(e,t);function a(e){v(i,o,n,a,c,"next",e)}function c(e){v(i,o,n,a,c,"throw",e)}a(void 0)})},function(){return u.apply(this,arguments)});return(0,a.useEffect)(function(){var e=b.get(o);if(e){var t=new URL(e);t.searchParams.set("post_logout_redirect_uri",window.location.href),b.remove(o,{domain:window.location.hostname}),window.location.href=t.href}var r=d.get("authenticator"),n=d.get("error");if(r===l.name&&n)return void i.message.error(p(n))}),c().createElement(i.Space,{direction:"vertical",className:(0,e.css)(h())},c().createElement(i.Button,{shape:"round",block:!0,icon:c().createElement(n.LoginOutlined,null),onClick:x},p(l.title)))},g=s(632),S=s(505),I={type:"object",properties:{public:{type:"object",properties:{autoSignup:{"x-decorator":"FormItem",type:"boolean",title:'{{t("Sign up automatically when the user does not exist")}}',"x-component":"Checkbox",default:!0}}},oidc:{type:"object",properties:{collapse:{type:"void","x-component":"FormTab",properties:{basic:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:p("Basic configuration")},properties:{issuer:{type:"string",title:'{{t("Issuer")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientId:{type:"string",title:'{{t("Client ID")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},clientSecret:{type:"string",title:'{{t("Client Secret")}}',"x-component":"Input","x-decorator":"FormItem",required:!0},scope:{type:"string",title:'{{t("scope")}}',"x-component":"Input","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Default: openid profile email")}}'}},idTokenSignedResponseAlg:{type:"string",title:'{{t("id_token signed response algorithm")}}',"x-component":"Select","x-decorator":"FormItem",enum:[{label:"HS256",value:"HS256"},{label:"HS384",value:"HS384"},{label:"HS512",value:"HS512"},{label:"RS256",value:"RS256"},{label:"RS384",value:"RS384"},{label:"RS512",value:"RS512"},{label:"ES256",value:"ES256"},{label:"ES384",value:"ES384"},{label:"ES512",value:"ES512"},{label:"PS256",value:"PS256"},{label:"PS384",value:"PS384"},{label:"PS512",value:"PS512"}]}}},mapping:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:p("Field mapping")},properties:{fieldMap:{title:'{{t("Field Map")}}',type:"array","x-decorator":"FormItem","x-component":"ArrayItems",items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{source:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("source")}}'}},target:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("target")}}'}},remove:{type:"void","x-decorator":"FormItem","x-component":"ArrayItems.Remove"}}}}},properties:{add:{type:"void",title:"Add","x-component":"ArrayItems.Addition"}}},userBindField:{type:"string",title:'{{t("Use this field to bind the user")}}',"x-component":"Select","x-decorator":"FormItem",default:"email",enum:[{label:p("Email"),value:"email"},{label:p("Username"),value:"username"}],required:!0}}},advanced:{type:"void","x-component":"FormTab.TabPane","x-component-props":{tab:p("Advanced configuration")},properties:{logout:{type:"boolean",title:'{{t("RP-initiated logout")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Performs logout on the issuer (uses end_session_endpoint in the issuer configuration)")}}'}},http:{type:"boolean",title:'{{t("HTTP")}}',"x-component":"Checkbox","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("Check if NocoBase is running on HTTP protocol")}}'}},port:{type:"number",title:'{{t("Port")}}',"x-component":"InputNumber","x-decorator":"FormItem","x-decorator-props":{tooltip:'{{t("The port number of the NocoBase service if it is not 80 or 443")}}'},"x-component-props":{style:{width:"15%",minWidth:"100px"}}},stateToken:{type:"string",title:'{{t("State token")}}',"x-component":"Input","x-decorator":"FormItem",description:p("The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation.")},exchangeBodyKeys:{type:"array",title:'{{t("Pass parameters in the authorization code grant exchange")}}',"x-decorator":"FormItem","x-component":"ArrayItems",default:[{paramName:"",optionsKey:"clientId"},{paramName:"",optionsKey:"clientSecret"}],items:{type:"object","x-decorator":"ArrayItems.Item",properties:{space:{type:"void","x-component":"Space",properties:{enabled:{type:"boolean","x-decorator":"FormItem","x-component":"Checkbox"},optionsKey:{type:"string","x-decorator":"FormItem","x-decorator-props":{style:{width:"100px"}},"x-component":"Select","x-read-pretty":!0,enum:[{label:p("Client ID"),value:"clientId"},{label:p("Client Secret"),value:"clientSecret"}]},paramName:{type:"string","x-decorator":"FormItem","x-component":"Input","x-component-props":{placeholder:'{{t("Parameter name")}}'}}}}}}},userInfoMethod:{type:"string",title:'{{t("Method to call the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"GET",enum:[{label:"GET",value:"GET"},{label:"POST",value:"POST"}],"x-reactions":[{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "query"}}',fulfill:{state:{value:"GET"}}},{dependencies:[".accessTokenVia"],when:'{{$deps[0] === "body"}}',fulfill:{state:{value:"POST"}}}]},accessTokenVia:{type:"string",title:'{{t("Where to put the access token when calling the user info endpoint")}}',"x- decorator":"FormItem","x-component":"Radio.Group",default:"header",enum:[{label:p("Header"),value:"header"},{label:p("Body (Use with POST method)"),value:"body"},{label:p("Query parameters (Use with GET method)"),value:"query"}]}}}}}}},usage:{type:"void","x-component":"Usage"}}},w=(0,S.observer)(function(){var t=f().t,r=(0,e.useApp)(),o=(0,a.useMemo)(function(){return r.getApiUrl("oidc:redirect")},[r]),u=function(e){navigator.clipboard.writeText(e),i.message.success(t("Copied"))};return c().createElement(i.Card,{title:t("Usage"),type:"inner"},c().createElement(e.FormItem,{label:t("Redirect URL")},c().createElement(e.Input,{value:o,disabled:!0,addonBefore:c().createElement(n.CopyOutlined,{onClick:function(){return u(o)}})})))},{displayName:"Usage"}),T=function(){var t=f().t;return c().createElement(e.SchemaComponent,{scope:{t:t},components:{Usage:w,ArrayItems:g.ArrayItems,Space:i.Space,FormTab:g.FormTab},schema:I})};function P(e,t,r,o,n,i,a){try{var c=e[i](a),u=c.value}catch(e){r(e);return}c.done?t(u):Promise.resolve(u).then(o,n)}function F(e,t,r){return(F=E()?Reflect.construct:function(e,t,r){var o=[null];o.push.apply(o,t);var n=new(Function.bind.apply(e,o));return r&&C(n,r.prototype),n}).apply(null,arguments)}function k(e){return(k=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function C(e,t){return(C=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function O(e){var t="function"==typeof Map?new Map:void 0;return(O=function(e){if(null===e||-1===Function.toString.call(e).indexOf("[native code]"))return e;if("function"!=typeof e)throw TypeError("Super expression must either be null or a function");if(void 0!==t){if(t.has(e))return t.get(e);t.set(e,r)}function r(){return F(e,arguments,k(this).constructor)}return r.prototype=Object.create(e.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}),C(r,e)})(e)}function E(){try{var e=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch(e){}return(E=function(){return!!e})()}var j=function(e){var t;if("function"!=typeof e&&null!==e)throw TypeError("Super expression must either be null or a function");function o(){var e,t;if(!(this instanceof o))throw TypeError("Cannot call a class as a function");return e=o,t=arguments,e=k(e),function(e,t){var r;if(t&&("object"==((r=t)&&"undefined"!=typeof Symbol&&r.constructor===Symbol?"symbol":typeof r)||"function"==typeof t))return t;if(void 0===e)throw ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(this,E()?Reflect.construct(e,t||[],k(this).constructor):e.apply(this,t))}return o.prototype=Object.create(e&&e.prototype,{constructor:{value:o,writable:!0,configurable:!0}}),e&&C(o,e),t=[{key:"load",value:function(){var e,t=this;return(e=function(){return function(e,t){var r,o,n,i,a={label:0,sent:function(){if(1&n[0])throw n[1];return n[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){var u=[i,c];if(r)throw TypeError("Generator is already executing.");for(;a;)try{if(r=1,o&&(n=2&u[0]?o.return:u[0]?o.throw||((n=o.return)&&n.call(o),0):o.next)&&!(n=n.call(o,u[1])).done)return n;switch(o=0,n&&(u=[2&u[0],n.value]),u[0]){case 0:case 1:n=u;break;case 4:return a.label++,{value:u[1],done:!1};case 5:a.label++,o=u[1],u=[0];continue;case 7:u=a.ops.pop(),a.trys.pop();continue;default:if(!(n=(n=a.trys).length>0&&n[n.length-1])&&(6===u[0]||2===u[0])){a=0;continue}if(3===u[0]&&(!n||u[1]>n[0]&&u[1]<n[3])){a.label=u[1];break}if(6===u[0]&&a.label<n[1]){a.label=n[1],n=u;break}if(n&&a.label<n[2]){a.label=n[2],a.ops.push(u);break}n[2]&&a.ops.pop(),a.trys.pop();continue}u=t.call(e,a)}catch(e){u=[6,e],o=0}finally{r=n=0}if(5&u[0])throw u[1];return{value:u[0]?u[1]:void 0,done:!0}}}}(this,function(e){return t.app.pm.get(r()).registerType("OIDC+",{components:{SignInButton:x,AdminSettingsForm:T}}),[2]})},function(){var t=this,r=arguments;return new Promise(function(o,n){var i=e.apply(t,r);function a(e){P(i,o,n,a,c,"next",e)}function c(e){P(i,o,n,a,c,"throw",e)}a(void 0)})})()}}],function(e,t){for(var r=0;r<t.length;r++){var o=t[r];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(e,o.key,o)}}(o.prototype,t),o}(O(e.Plugin)),R=j}(),d}()});
|
package/dist/externalVersion.js
CHANGED
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
module.exports = {
|
|
11
|
-
"@ant-design/icons": "5.
|
|
12
|
-
"@nocobase/client": "1.
|
|
13
|
-
"antd": "5.
|
|
11
|
+
"@ant-design/icons": "5.6.1",
|
|
12
|
+
"@nocobase/client": "1.8.23",
|
|
13
|
+
"antd": "5.24.2",
|
|
14
14
|
"react": "18.2.0",
|
|
15
|
-
"react-router-dom": "6.
|
|
16
|
-
"@nocobase/plugin-auth": "1.
|
|
17
|
-
"@formily/antd-v5": "1.
|
|
15
|
+
"react-router-dom": "6.28.1",
|
|
16
|
+
"@nocobase/plugin-auth": "1.8.23",
|
|
17
|
+
"@formily/antd-v5": "1.2.3",
|
|
18
18
|
"@formily/react": "2.3.0",
|
|
19
|
-
"@nocobase/auth": "1.
|
|
20
|
-
"@nocobase/server": "1.
|
|
19
|
+
"@nocobase/auth": "1.8.23",
|
|
20
|
+
"@nocobase/server": "1.8.23",
|
|
21
21
|
"react-i18next": "11.18.6",
|
|
22
|
-
"@nocobase/actions": "1.
|
|
22
|
+
"@nocobase/actions": "1.8.23"
|
|
23
23
|
};
|
package/package.json
CHANGED
|
@@ -2,29 +2,29 @@
|
|
|
2
2
|
"name": "@typekcz-nocobase-plugins/plugin-oidc-plus",
|
|
3
3
|
"displayName": "Auth: OIDC Plus",
|
|
4
4
|
"description": "OIDC (OpenID Connect) authentication with extra features.",
|
|
5
|
-
"version": "1.0.
|
|
5
|
+
"version": "1.0.4",
|
|
6
6
|
"license": "AGPL-3.0",
|
|
7
7
|
"main": "dist/server/index.js",
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@ant-design/icons": "5.x",
|
|
10
10
|
"@formily/antd-v5": "1.x",
|
|
11
11
|
"@formily/react": "2.x",
|
|
12
|
-
"ahooks": "3.7.2",
|
|
12
|
+
"ahooks": "^3.7.2",
|
|
13
13
|
"antd": "5.x",
|
|
14
|
-
"nanoid": "3.3.4",
|
|
14
|
+
"nanoid": "^3.3.4",
|
|
15
15
|
"openid-client": "^5.4.2",
|
|
16
16
|
"react": "18.x",
|
|
17
17
|
"react-i18next": "^11.15.1",
|
|
18
18
|
"js-cookie": "^3.0.5"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
|
-
"@nocobase/actions": "
|
|
22
|
-
"@nocobase/auth": "
|
|
23
|
-
"@nocobase/client": "
|
|
24
|
-
"@nocobase/database": "
|
|
25
|
-
"@nocobase/plugin-auth": "
|
|
26
|
-
"@nocobase/server": "
|
|
27
|
-
"@nocobase/test": "
|
|
21
|
+
"@nocobase/actions": "1.x",
|
|
22
|
+
"@nocobase/auth": "1.x",
|
|
23
|
+
"@nocobase/client": "1.x",
|
|
24
|
+
"@nocobase/database": "1.x",
|
|
25
|
+
"@nocobase/plugin-auth": "1.x",
|
|
26
|
+
"@nocobase/server": "1.x",
|
|
27
|
+
"@nocobase/test": "1.x"
|
|
28
28
|
},
|
|
29
29
|
"keywords": [
|
|
30
30
|
"Authentication"
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { LoginOutlined } from '@ant-design/icons';
|
|
2
|
+
import { css, useAPIClient } from '@nocobase/client';
|
|
3
|
+
import { Button, Space, message } from 'antd';
|
|
4
|
+
import React, { useEffect } from 'react';
|
|
5
|
+
import { useOidcTranslation } from './locale';
|
|
6
|
+
import { useLocation } from 'react-router-dom';
|
|
7
|
+
import { Authenticator } from '@nocobase/plugin-auth/client';
|
|
8
|
+
import Cookies from 'js-cookie';
|
|
9
|
+
import { logoutCookieName } from '../constants';
|
|
10
|
+
|
|
11
|
+
export interface OIDCProvider {
|
|
12
|
+
clientId: string;
|
|
13
|
+
title: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const OIDCButton = ({ authenticator }: { authenticator: Authenticator }) => {
|
|
17
|
+
const { t } = useOidcTranslation();
|
|
18
|
+
const api = useAPIClient();
|
|
19
|
+
const location = useLocation();
|
|
20
|
+
const params = new URLSearchParams(location.search);
|
|
21
|
+
const redirect = params.get('redirect');
|
|
22
|
+
|
|
23
|
+
const login = async () => {
|
|
24
|
+
const response = await api.request({
|
|
25
|
+
method: 'post',
|
|
26
|
+
url: 'oidc:getAuthUrl',
|
|
27
|
+
headers: {
|
|
28
|
+
'X-Authenticator': authenticator.name,
|
|
29
|
+
},
|
|
30
|
+
data: {
|
|
31
|
+
redirect,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const authUrl = response?.data?.data;
|
|
36
|
+
window.location.replace(authUrl);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const logoutUrl = Cookies.get(logoutCookieName);
|
|
41
|
+
if (logoutUrl) {
|
|
42
|
+
const logoutUrlObj = new URL(logoutUrl);
|
|
43
|
+
logoutUrlObj.searchParams.set('post_logout_redirect_uri', window.location.href);
|
|
44
|
+
Cookies.remove(logoutCookieName, { domain: window.location.hostname });
|
|
45
|
+
window.location.href = logoutUrlObj.href;
|
|
46
|
+
}
|
|
47
|
+
const name = params.get('authenticator');
|
|
48
|
+
const error = params.get('error');
|
|
49
|
+
if (name !== authenticator.name) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (error) {
|
|
53
|
+
message.error(t(error));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Space
|
|
60
|
+
direction="vertical"
|
|
61
|
+
className={css`
|
|
62
|
+
display: flex;
|
|
63
|
+
`}
|
|
64
|
+
>
|
|
65
|
+
<Button shape="round" block icon={<LoginOutlined />} onClick={login}>
|
|
66
|
+
{t(authenticator.title)}
|
|
67
|
+
</Button>
|
|
68
|
+
</Space>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { CopyOutlined } from '@ant-design/icons';
|
|
2
|
+
import { ArrayItems, FormTab } from '@formily/antd-v5';
|
|
3
|
+
import { observer } from '@formily/react';
|
|
4
|
+
import { FormItem, Input, SchemaComponent, useApp } from '@nocobase/client';
|
|
5
|
+
import { Card, Space, message } from 'antd';
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
7
|
+
import { lang, useOidcTranslation } from './locale';
|
|
8
|
+
|
|
9
|
+
const schema = {
|
|
10
|
+
type: 'object',
|
|
11
|
+
properties: {
|
|
12
|
+
public: {
|
|
13
|
+
type: 'object',
|
|
14
|
+
properties: {
|
|
15
|
+
autoSignup: {
|
|
16
|
+
'x-decorator': 'FormItem',
|
|
17
|
+
type: 'boolean',
|
|
18
|
+
title: '{{t("Sign up automatically when the user does not exist")}}',
|
|
19
|
+
'x-component': 'Checkbox',
|
|
20
|
+
default: true,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
oidc: {
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
collapse: {
|
|
28
|
+
type: 'void',
|
|
29
|
+
'x-component': 'FormTab',
|
|
30
|
+
properties: {
|
|
31
|
+
basic: {
|
|
32
|
+
type: 'void',
|
|
33
|
+
'x-component': 'FormTab.TabPane',
|
|
34
|
+
'x-component-props': {
|
|
35
|
+
tab: lang('Basic configuration'),
|
|
36
|
+
},
|
|
37
|
+
properties: {
|
|
38
|
+
issuer: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
title: '{{t("Issuer")}}',
|
|
41
|
+
'x-component': 'Input',
|
|
42
|
+
'x-decorator': 'FormItem',
|
|
43
|
+
required: true,
|
|
44
|
+
},
|
|
45
|
+
clientId: {
|
|
46
|
+
type: 'string',
|
|
47
|
+
title: '{{t("Client ID")}}',
|
|
48
|
+
'x-component': 'Input',
|
|
49
|
+
'x-decorator': 'FormItem',
|
|
50
|
+
required: true,
|
|
51
|
+
},
|
|
52
|
+
clientSecret: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
title: '{{t("Client Secret")}}',
|
|
55
|
+
'x-component': 'Input',
|
|
56
|
+
'x-decorator': 'FormItem',
|
|
57
|
+
required: true,
|
|
58
|
+
},
|
|
59
|
+
scope: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
title: '{{t("scope")}}',
|
|
62
|
+
'x-component': 'Input',
|
|
63
|
+
'x-decorator': 'FormItem',
|
|
64
|
+
'x-decorator-props': {
|
|
65
|
+
tooltip: '{{t("Default: openid profile email")}}',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
idTokenSignedResponseAlg: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
title: '{{t("id_token signed response algorithm")}}',
|
|
71
|
+
'x-component': 'Select',
|
|
72
|
+
'x-decorator': 'FormItem',
|
|
73
|
+
enum: [
|
|
74
|
+
{ label: 'HS256', value: 'HS256' },
|
|
75
|
+
{ label: 'HS384', value: 'HS384' },
|
|
76
|
+
{ label: 'HS512', value: 'HS512' },
|
|
77
|
+
{ label: 'RS256', value: 'RS256' },
|
|
78
|
+
{ label: 'RS384', value: 'RS384' },
|
|
79
|
+
{ label: 'RS512', value: 'RS512' },
|
|
80
|
+
{ label: 'ES256', value: 'ES256' },
|
|
81
|
+
{ label: 'ES384', value: 'ES384' },
|
|
82
|
+
{ label: 'ES512', value: 'ES512' },
|
|
83
|
+
{ label: 'PS256', value: 'PS256' },
|
|
84
|
+
{ label: 'PS384', value: 'PS384' },
|
|
85
|
+
{ label: 'PS512', value: 'PS512' },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
mapping: {
|
|
91
|
+
type: 'void',
|
|
92
|
+
'x-component': 'FormTab.TabPane',
|
|
93
|
+
'x-component-props': {
|
|
94
|
+
tab: lang('Field mapping'),
|
|
95
|
+
},
|
|
96
|
+
properties: {
|
|
97
|
+
fieldMap: {
|
|
98
|
+
title: '{{t("Field Map")}}',
|
|
99
|
+
type: 'array',
|
|
100
|
+
'x-decorator': 'FormItem',
|
|
101
|
+
'x-component': 'ArrayItems',
|
|
102
|
+
items: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
'x-decorator': 'ArrayItems.Item',
|
|
105
|
+
properties: {
|
|
106
|
+
space: {
|
|
107
|
+
type: 'void',
|
|
108
|
+
'x-component': 'Space',
|
|
109
|
+
properties: {
|
|
110
|
+
source: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
'x-decorator': 'FormItem',
|
|
113
|
+
'x-component': 'Input',
|
|
114
|
+
'x-component-props': {
|
|
115
|
+
placeholder: '{{t("source")}}',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
target: {
|
|
119
|
+
type: 'string',
|
|
120
|
+
'x-decorator': 'FormItem',
|
|
121
|
+
'x-component': 'Input',
|
|
122
|
+
'x-component-props': {
|
|
123
|
+
placeholder: '{{t("target")}}',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
remove: {
|
|
127
|
+
type: 'void',
|
|
128
|
+
'x-decorator': 'FormItem',
|
|
129
|
+
'x-component': 'ArrayItems.Remove',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
properties: {
|
|
136
|
+
add: {
|
|
137
|
+
type: 'void',
|
|
138
|
+
title: 'Add',
|
|
139
|
+
'x-component': 'ArrayItems.Addition',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
userBindField: {
|
|
144
|
+
type: 'string',
|
|
145
|
+
title: '{{t("Use this field to bind the user")}}',
|
|
146
|
+
'x-component': 'Select',
|
|
147
|
+
'x-decorator': 'FormItem',
|
|
148
|
+
default: 'email',
|
|
149
|
+
enum: [
|
|
150
|
+
{ label: lang('Email'), value: 'email' },
|
|
151
|
+
{ label: lang('Username'), value: 'username' },
|
|
152
|
+
],
|
|
153
|
+
required: true,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
advanced: {
|
|
158
|
+
type: 'void',
|
|
159
|
+
'x-component': 'FormTab.TabPane',
|
|
160
|
+
'x-component-props': {
|
|
161
|
+
tab: lang('Advanced configuration'),
|
|
162
|
+
},
|
|
163
|
+
properties: {
|
|
164
|
+
logout: {
|
|
165
|
+
type: 'boolean',
|
|
166
|
+
title: '{{t("RP-initiated logout")}}',
|
|
167
|
+
'x-component': 'Checkbox',
|
|
168
|
+
'x-decorator': 'FormItem',
|
|
169
|
+
'x-decorator-props': {
|
|
170
|
+
tooltip:
|
|
171
|
+
'{{t("Performs logout on the issuer (uses end_session_endpoint in the issuer configuration)")}}',
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
http: {
|
|
175
|
+
type: 'boolean',
|
|
176
|
+
title: '{{t("HTTP")}}',
|
|
177
|
+
'x-component': 'Checkbox',
|
|
178
|
+
'x-decorator': 'FormItem',
|
|
179
|
+
'x-decorator-props': {
|
|
180
|
+
tooltip: '{{t("Check if NocoBase is running on HTTP protocol")}}',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
port: {
|
|
184
|
+
type: 'number',
|
|
185
|
+
title: '{{t("Port")}}',
|
|
186
|
+
'x-component': 'InputNumber',
|
|
187
|
+
'x-decorator': 'FormItem',
|
|
188
|
+
'x-decorator-props': {
|
|
189
|
+
tooltip: '{{t("The port number of the NocoBase service if it is not 80 or 443")}}',
|
|
190
|
+
},
|
|
191
|
+
'x-component-props': {
|
|
192
|
+
style: {
|
|
193
|
+
width: '15%',
|
|
194
|
+
minWidth: '100px',
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
stateToken: {
|
|
199
|
+
type: 'string',
|
|
200
|
+
title: '{{t("State token")}}',
|
|
201
|
+
'x-component': 'Input',
|
|
202
|
+
'x-decorator': 'FormItem',
|
|
203
|
+
description: lang(
|
|
204
|
+
"The state token helps prevent CSRF attacks. It's recommended to leave it blank for automatic random generation.",
|
|
205
|
+
),
|
|
206
|
+
},
|
|
207
|
+
exchangeBodyKeys: {
|
|
208
|
+
type: 'array',
|
|
209
|
+
title: '{{t("Pass parameters in the authorization code grant exchange")}}',
|
|
210
|
+
'x-decorator': 'FormItem',
|
|
211
|
+
'x-component': 'ArrayItems',
|
|
212
|
+
default: [
|
|
213
|
+
{ paramName: '', optionsKey: 'clientId' },
|
|
214
|
+
{
|
|
215
|
+
paramName: '',
|
|
216
|
+
optionsKey: 'clientSecret',
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
items: {
|
|
220
|
+
type: 'object',
|
|
221
|
+
'x-decorator': 'ArrayItems.Item',
|
|
222
|
+
properties: {
|
|
223
|
+
space: {
|
|
224
|
+
type: 'void',
|
|
225
|
+
'x-component': 'Space',
|
|
226
|
+
properties: {
|
|
227
|
+
enabled: {
|
|
228
|
+
type: 'boolean',
|
|
229
|
+
'x-decorator': 'FormItem',
|
|
230
|
+
'x-component': 'Checkbox',
|
|
231
|
+
},
|
|
232
|
+
optionsKey: {
|
|
233
|
+
type: 'string',
|
|
234
|
+
'x-decorator': 'FormItem',
|
|
235
|
+
'x-decorator-props': {
|
|
236
|
+
style: {
|
|
237
|
+
width: '100px',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
'x-component': 'Select',
|
|
241
|
+
'x-read-pretty': true,
|
|
242
|
+
enum: [
|
|
243
|
+
{ label: lang('Client ID'), value: 'clientId' },
|
|
244
|
+
{ label: lang('Client Secret'), value: 'clientSecret' },
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
paramName: {
|
|
248
|
+
type: 'string',
|
|
249
|
+
'x-decorator': 'FormItem',
|
|
250
|
+
'x-component': 'Input',
|
|
251
|
+
'x-component-props': {
|
|
252
|
+
placeholder: '{{t("Parameter name")}}',
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
userInfoMethod: {
|
|
261
|
+
type: 'string',
|
|
262
|
+
title: '{{t("Method to call the user info endpoint")}}',
|
|
263
|
+
'x- decorator': 'FormItem',
|
|
264
|
+
'x-component': 'Radio.Group',
|
|
265
|
+
default: 'GET',
|
|
266
|
+
enum: [
|
|
267
|
+
{
|
|
268
|
+
label: 'GET',
|
|
269
|
+
value: 'GET',
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
label: 'POST',
|
|
273
|
+
value: 'POST',
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
'x-reactions': [
|
|
277
|
+
{
|
|
278
|
+
dependencies: ['.accessTokenVia'],
|
|
279
|
+
when: '{{$deps[0] === "query"}}',
|
|
280
|
+
fulfill: {
|
|
281
|
+
state: {
|
|
282
|
+
value: 'GET',
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
dependencies: ['.accessTokenVia'],
|
|
288
|
+
when: '{{$deps[0] === "body"}}',
|
|
289
|
+
fulfill: {
|
|
290
|
+
state: {
|
|
291
|
+
value: 'POST',
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
},
|
|
297
|
+
accessTokenVia: {
|
|
298
|
+
type: 'string',
|
|
299
|
+
title: '{{t("Where to put the access token when calling the user info endpoint")}}',
|
|
300
|
+
'x- decorator': 'FormItem',
|
|
301
|
+
'x-component': 'Radio.Group',
|
|
302
|
+
default: 'header',
|
|
303
|
+
enum: [
|
|
304
|
+
{
|
|
305
|
+
label: lang('Header'),
|
|
306
|
+
value: 'header',
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
label: lang('Body (Use with POST method)'),
|
|
310
|
+
value: 'body',
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
label: lang('Query parameters (Use with GET method)'),
|
|
314
|
+
value: 'query',
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
usage: {
|
|
325
|
+
type: 'void',
|
|
326
|
+
'x-component': 'Usage',
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const Usage = observer(
|
|
332
|
+
() => {
|
|
333
|
+
const { t } = useOidcTranslation();
|
|
334
|
+
const app = useApp();
|
|
335
|
+
|
|
336
|
+
const url = useMemo(() => {
|
|
337
|
+
return app.getApiUrl('oidc:redirect');
|
|
338
|
+
}, [app]);
|
|
339
|
+
|
|
340
|
+
const copy = (text: string) => {
|
|
341
|
+
navigator.clipboard.writeText(text);
|
|
342
|
+
message.success(t('Copied'));
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<Card title={t('Usage')} type="inner">
|
|
347
|
+
<FormItem label={t('Redirect URL')}>
|
|
348
|
+
<Input value={url} disabled={true} addonBefore={<CopyOutlined onClick={() => copy(url)} />} />
|
|
349
|
+
</FormItem>
|
|
350
|
+
</Card>
|
|
351
|
+
);
|
|
352
|
+
},
|
|
353
|
+
{ displayName: 'Usage' },
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
export const Options = () => {
|
|
357
|
+
const { t } = useOidcTranslation();
|
|
358
|
+
return <SchemaComponent scope={{ t }} components={{ Usage, ArrayItems, Space, FormTab }} schema={schema} />;
|
|
359
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Plugin } from '@nocobase/client';
|
|
2
|
+
import AuthPlugin from '@nocobase/plugin-auth/client';
|
|
3
|
+
import { authType } from '../constants';
|
|
4
|
+
import { OIDCButton } from './OIDCButton';
|
|
5
|
+
import { Options } from './Options';
|
|
6
|
+
|
|
7
|
+
export class PluginOIDCClient extends Plugin {
|
|
8
|
+
async load() {
|
|
9
|
+
const auth = this.app.pm.get(AuthPlugin);
|
|
10
|
+
auth.registerType(authType, {
|
|
11
|
+
components: {
|
|
12
|
+
SignInButton: OIDCButton,
|
|
13
|
+
AdminSettingsForm: Options,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default PluginOIDCClient;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { i18n } from '@nocobase/client';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
|
|
4
|
+
export const NAMESPACE = 'oidc';
|
|
5
|
+
|
|
6
|
+
// i18n.addResources('zh-CN', NAMESPACE, zhCN);
|
|
7
|
+
// i18n.addResources('en-US', NAMESPACE, enUS);
|
|
8
|
+
// i18n.addResources('ja-JP', NAMESPACE, jaJP);
|
|
9
|
+
// i18n.addResources('ru-RU', NAMESPACE, ruRU);
|
|
10
|
+
// i18n.addResources('tr-TR', NAMESPACE, trTR);
|
|
11
|
+
|
|
12
|
+
export function lang(key: string) {
|
|
13
|
+
return i18n.t(key, { ns: NAMESPACE });
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function useOidcTranslation() {
|
|
17
|
+
return useTranslation(NAMESPACE);
|
|
18
|
+
}
|
package/src/constants.ts
ADDED
package/src/index.ts
ADDED