├── example
├── app.js
└── index.html
├── .npmignore
├── .gitignore
├── assets
└── 3id-connect_readme-image.png
├── Dockerfile.example
├── .babelrc
├── src
├── index.js
├── authProvider
│ ├── ethereumAuthProvider.js
│ └── abstractAuthProvider.js
├── utils.js
├── threeIdProviderProxy.js
├── threeIdConnect.js
└── threeIdConnectService.js
├── .dependabot
└── config.yml
├── LICENSE-APACHE
├── public
└── index.html
├── LICENSE-MIT
├── webpack.config.js
├── iframe
├── html
│ └── template.js
├── index.js
├── css
│ ├── variables.scss
│ └── style.scss
└── assets
│ ├── logo.svg
│ └── assets.js
├── package.json
├── README.md
└── .circleci
└── config.yml
/example/app.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | public
3 | iframe
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | public/index.js
3 | lib
4 |
5 | .idea
--------------------------------------------------------------------------------
/assets/3id-connect_readme-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3box/3id-connect/HEAD/assets/3id-connect_readme-image.png
--------------------------------------------------------------------------------
/Dockerfile.example:
--------------------------------------------------------------------------------
1 | FROM node:10
2 |
3 | WORKDIR /3id-connect
4 |
5 | COPY package.json package-lock.json ./
6 |
7 | COPY src ./src
8 | COPY webpack*.config.js .babelrc ./
9 | COPY public ./public
10 |
11 | EXPOSE 30001
12 |
13 | CMD npm run start
14 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 |
3 | "presets": ["@babel/preset-env", "@babel/preset-react"],
4 | "plugins": [
5 | "@babel/plugin-transform-runtime",
6 | "@babel/plugin-proposal-object-rest-spread",
7 | "@babel/plugin-transform-modules-commonjs"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import ThreeIdProviderProxy from './threeIdProviderProxy.js'
2 | import ThreeIdConnectService from './threeIdConnectService.js'
3 | import ThreeIdConnect from './threeIdConnect.js'
4 |
5 | export {
6 | ThreeIdConnect,
7 | ThreeIdConnectService,
8 | ThreeIdProviderProxy
9 | }
10 |
--------------------------------------------------------------------------------
/.dependabot/config.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | update_configs:
3 | - package_manager: "javascript"
4 | target_branch: "develop"
5 | directory: "/"
6 | update_schedule: "weekly"
7 | default_reviewers:
8 | - "zachferland"
9 | default_assignees:
10 | - "zachferland"
11 | allowed_updates:
12 | - match:
13 | dependency_type: "production"
14 | update_type: "all"
15 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | 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
2 |
3 | http://www.apache.org/licenses/LICENSE-2.0
4 |
5 | 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.
6 |
--------------------------------------------------------------------------------
/src/authProvider/ethereumAuthProvider.js:
--------------------------------------------------------------------------------
1 | import { createLink, authenticate } from '3id-blockchain-utils'
2 | import AbstractAuthProvider from './abstractAuthProvider'
3 |
4 | /**
5 | * AuthProvider which can be used for ethreum providers with standard interface
6 | */
7 | class EthereumAuthProvider extends AbstractAuthProvider {
8 | constructor(ethProvider) {
9 | super()
10 | this.network = 'ethereum'
11 | this.provider = ethProvider
12 | }
13 |
14 | async authenticate(message, accountId) {
15 | return authenticate(message, accountId, this.provider)
16 | }
17 |
18 | async createLink(did, accountId) {
19 | return createLink(did, accountId, this.provider, { type: 'ethereum-eoa' })
20 | }
21 | }
22 |
23 | export default EthereumAuthProvider
24 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 3Box Account
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 3Box Account
8 |
9 |
10 |
11 |
12 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | // Partically redundant with 3boxjs utils, but added to remove circular dependency entirely for now
2 |
3 | const HTTPError = (status, message) => {
4 | const e = new Error(message)
5 | e.statusCode = status
6 | return e
7 | }
8 |
9 | const fetchJson = async (url, body) => {
10 | let opts
11 | if (body) {
12 | opts = { body: JSON.stringify(body), method: 'POST', headers: { 'Content-Type': 'application/json' } }
13 | }
14 | const r = await window.fetch(url, opts)
15 |
16 | if (r.ok) {
17 | let res = await r.json()
18 | return res
19 | } else {
20 | throw HTTPError(r.status, (await r.json()).message)
21 | }
22 | }
23 |
24 | const isLinked = async (address) => {
25 | try {
26 | const res = await fetchJson(`https://beta.3box.io/address-server/odbAddress/${address}`)
27 | return Boolean(res.data.rootStoreAddress)
28 | } catch (err) {
29 | return false
30 | }
31 | }
32 |
33 | export {
34 | fetchJson,
35 | isLinked
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
--------------------------------------------------------------------------------
/src/threeIdProviderProxy.js:
--------------------------------------------------------------------------------
1 | import { caller } from 'postmsg-rpc'
2 |
3 | const callbackOrThrow = (callback, errMsg) => {
4 | if (callback) {
5 | callback(errMsg)
6 | } else {
7 | throw errMsg instanceof Error ? errMsg : new Error(errMsg)
8 | }
9 | }
10 |
11 | /**
12 | * A 3ID provider proxy, 3ID provider interface that acts as rpc client, to
13 | * relay request to iframe (rpc server)
14 | */
15 | class ThreeIdProviderProxy {
16 | constructor (postMessage) {
17 | this.postMessage = postMessage
18 | this.is3idProvider = true
19 | this.threeIdConnect = true
20 | this.migration = true
21 | this.sendRPC = caller('send', {postMessage: this.postMessage})
22 | }
23 |
24 | async send (req, origin, callback) {
25 | if (typeof origin === 'function') {
26 | callback = origin
27 | origin = null
28 | }
29 |
30 | // Catches rpc errors, method errors are relayed in response for client to handle
31 | try {
32 | const res = JSON.parse(await this.sendRPC(req))
33 | if (callback) callback(undefined, res)
34 | return res
35 | } catch (err) {
36 | callbackOrThrow(callback, err)
37 | return
38 | }
39 | }
40 | }
41 |
42 | export default ThreeIdProviderProxy
43 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | entry: './iframe/index.js',
5 | output: {
6 | filename: 'index.js',
7 | path: path.resolve(__dirname, 'public'),
8 | libraryTarget: 'umd',
9 | umdNamedDefine: true
10 | },
11 | module: {
12 | rules: [{
13 | test: /\.js$/,
14 | exclude: /(node_modules)/,
15 | use: {
16 | loader: 'babel-loader',
17 | options: {
18 | presets: ['@babel/preset-env'],
19 | plugins: [
20 | ['@babel/plugin-transform-runtime', {
21 | 'regenerator': true
22 | }],
23 | ['@babel/plugin-proposal-object-rest-spread']
24 | ]
25 | }
26 | }
27 | },
28 | {
29 | test: /\.scss$/,
30 | use: [{
31 | loader: "css-loader",
32 | options: {
33 | sourceMap: true,
34 | modules: true,
35 | localIdentName: "[local]"
36 | }
37 | },
38 | {
39 | loader: "sass-loader"
40 | }
41 | ]
42 | },
43 | {
44 | test: /\.(png|woff|woff2|eot|ttf)$/,
45 | loader: 'url-loader'
46 | },
47 | {
48 | test: /\.svg$/,
49 | loader: 'svg-inline-loader'
50 | },
51 | ]
52 | },
53 | node: {
54 | console: false,
55 | fs: 'empty',
56 | net: 'empty',
57 | tls: 'empty',
58 | child_process: 'empty'
59 | }
60 | };
61 |
--------------------------------------------------------------------------------
/iframe/html/template.js:
--------------------------------------------------------------------------------
1 | const style = require('style-loader!../css/style.scss')
2 | const assets = require('./../assets/assets.js')
3 |
4 | const capitalizeFirst = string => string.charAt(0).toUpperCase() + string.slice(1)
5 | const spaceString = (spaces) => spaces.join(', ')
6 |
7 | const template = (data,isMobile) => `
8 |
9 |
27 |
28 |
29 |
44 |
51 |
52 |
53 | `
54 | export default template
55 |
56 | const error = (data) => `
57 | ${data.error}
58 | `
59 |
--------------------------------------------------------------------------------
/iframe/index.js:
--------------------------------------------------------------------------------
1 | import template from './html/template.js'
2 | import ThreeIdConnectService from './../src/threeIdConnectService.js'
3 | const store = require('store')
4 | const assets = require('./assets/assets.js')
5 |
6 | /**
7 | * UI Window Functions
8 | */
9 |
10 | const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
11 | const checkIsMobile = () => mobileRegex.test(navigator.userAgent)
12 |
13 | const error = (error) => `
14 | ${error}
15 | `
16 |
17 | // Given a request will render UI module templates
18 | const render = async (request) => {
19 | document.getElementById('root').innerHTML = template({request}, checkIsMobile())
20 | }
21 |
22 | /**
23 | * Identity Wallet Service configuration and start
24 | */
25 |
26 | const idwService = new ThreeIdConnectService()
27 |
28 | // IDW getConsent function. Consume IDW request, renders request to user, and resolve selection
29 | const getConsent = async (req) => {
30 | await idwService.displayIframe()
31 | await render(req)
32 | const accept = document.getElementById('accept')
33 |
34 | const result = await new Promise((resolve, reject) => {
35 | accept.addEventListener('click', () => {
36 | accept.innerHTML = `Confirm in your wallet ${assets.Loading}`;
37 | accept.style.boxShadow = 'none';
38 | resolve(true)
39 | })
40 | })
41 |
42 | return result
43 | }
44 |
45 | // Service calls on error, renders error to UI
46 | const errorCb = (err, msg, req) => {
47 | if (!msg) msg = err.toString()
48 | msg = 'Error: Unable to connect'
49 | console.log(err)
50 | document.getElementById('action').innerHTML = error(msg)
51 | }
52 |
53 | // Closure to pass cancel state to IDW iframe service
54 | let closecallback
55 |
56 | window.hideIframe = () => {
57 | idwService.hideIframe()
58 | const root = document.getElementById('root')
59 | if (root) root.innerHTML = ``
60 | if (closecallback) closecallback()
61 | }
62 |
63 | const closing = (cb) => {
64 | closecallback = cb
65 | }
66 |
67 | idwService.start(getConsent, errorCb, closing)
68 |
69 | // For testing, uncomment one line to see static view
70 | // render(JSON.parse(`{"type":"authenticate","origin":"localhost:30001","spaces":["metamask", "3Box", "thingspace"], "opts": { "address": "0x9acb0539f2ea0c258ac43620dd03ef01f676a69b"}}`))
71 |
--------------------------------------------------------------------------------
/iframe/css/variables.scss:
--------------------------------------------------------------------------------
1 | $navHeight: 70px;
2 | $sideBarWidth: 200px;
3 |
4 | $inputPadding: 12px 14px;
5 |
6 | // Brand Colors
7 | $threeBoxBlue: #1168df;
8 | $threeBoxBlueHover: #0950b0;
9 | $textColor: #727581;
10 |
11 | // ThreeId Brand Colors
12 | // $threeIdOrange: #F76837;
13 | // $textColor: #817972;
14 |
15 | $symbolGrey: #D8D8D8;
16 |
17 | $indexModalPreviewImageHeight: 100px;
18 | $hoverBackground: #efefef;
19 |
20 | $hoverBackgroundDark: #e6e6e6;
21 |
22 | $editorBG: #4a4a4a;
23 |
24 | $radius: 5px;
25 |
26 | $threeBoxBlack: #181F21;
27 | $threeBoxBlackHover: rgb(47, 56, 58);
28 |
29 | $messageBackground: #1167df29;
30 | $blueBorderColor: #1167dfc6;
31 |
32 | $transition: all ease-in-out .3s;
33 |
34 | $sectionMargin: 40px;
35 | // $threeBoxBlueDeep: #004AA8;
36 | // $threeBoxBlueDeeper: #00357D;
37 | // $threeBoxBlueLight: #CBDBEE;
38 | // $threeBoxBlueLightTransparency: rgba(187, 218, 255, 0.493);
39 |
40 | // $threeBoxAccentBlue: #021333;
41 | // $threeBoxAccentBlueHover: #254277;
42 |
43 | // // Brand Blue Hues
44 | // $threeBoxBlueShade: #C5D7F2;
45 | // $threeBoxBlueLightBG: rgb(242, 248, 255);
46 | // $threeBoxBlueHue: rgb(251, 254, 255);
47 | // $threeBoxBlueHueSecondary: #EDEEEF;
48 |
49 | // $threeBoxBlueLightShade: #EFF5FE;
50 |
51 | // $turquoise: #8CFFDE;
52 | // $pink: #FF8AFF;
53 | // $yellow: #FEFE69;
54 |
55 | // $backgroundGrey: rgb(248, 248, 248);
56 | // $backgroundGreyDark: #dfdfdfa6;
57 |
58 | $backgroundHue: #F5F6FA;
59 | $backgroundHueDark: #e0e2e8;
60 | $backgroundHueDarkBorderColor: #c8cad2;
61 | // $backgroundHue: rgb(245, 246, 248);
62 |
63 | $threeBoxRed: rgb(202, 50, 50);
64 | // $threeBoxRedHover: rgb(155, 36, 36);
65 | // $threeBoxGreen: rgb(103, 204, 125);
66 |
67 |
68 | // // Font Colors
69 | // $lightFont: #6b6b6b;
70 | // $lightFontHover: #525252;
71 | $lighterFont: #747677;
72 | $lightestFont: #b1b1b1;
73 |
74 |
75 | // $faintFont: #b8b8b8;
76 | // $lightBlueFont: rgb(79, 117, 153);
77 | // $medFont: #546983;
78 |
79 | $borderColor: rgb(225, 225, 225);
80 | // $borderColorDark:#dcdcdc;
81 |
82 | // $feedTileGrey: rgb(249, 249, 249);
83 |
84 | // $defaultProfPic:#006ECC;
85 |
86 | $dropShadow: 0px 4px 10px 4px #00000025,
87 | // 0 16px 24px 0 #0000002f;
88 |
89 | // $maxCommentWidth: 600px;
90 | // $minCommentWidth: 300px;
91 |
92 | // $minCommentWidth-mobile: 260px;
93 |
94 | // $commentPictureDimension: 40px;
95 |
96 | // $commentPictureDimension-mobile: 50px;
97 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "3id-connect",
3 | "version": "0.1.0",
4 | "description": "Account management for 3Box",
5 | "main": "lib/index.js",
6 | "directories": {
7 | "lib": "lib"
8 | },
9 | "scripts": {
10 | "lint": "./node_modules/.bin/standard --verbose src/**",
11 | "test": "jest --detectOpenHandles --coverage",
12 | "build:es5": "rm -rf ./lib; ./node_modules/.bin/babel src --out-dir lib",
13 | "build:dist": "./node_modules/.bin/webpack --config webpack.config.js --mode=development",
14 | "build:dist:watch": "./node_modules/.bin/webpack --config webpack.config.js --mode=development --watch",
15 | "build:dist:prod": "./node_modules/.bin/webpack --config webpack.config.js --mode=production --output-filename index.js",
16 | "build": "npm run build:dist:prod; npm run build:es5;",
17 | "prepublishOnly": "npm run build:es5",
18 | "prepare": "npm run build:es5",
19 | "server:start": "http-server -c-1 -p 30001 public",
20 | "start": "npm run build:dist:watch & npm run server:start"
21 | },
22 | "jest": {
23 | "testEnvironment": "node",
24 | "testPathIgnorePatterns": [
25 | "node_modules",
26 | "lib"
27 | ]
28 | },
29 | "repository": {
30 | "type": "git",
31 | "url": "git+https://github.com/3box/3id-connect.git"
32 | },
33 | "author": "3box ",
34 | "license": "(Apache-2.0 OR MIT)",
35 | "bugs": {
36 | "url": "https://github.com/3box/3id-connect/issues"
37 | },
38 | "homepage": "https://github.com/3box/3id-connect#readme",
39 | "dependencies": {
40 | "3id-blockchain-utils": "^0.4.0",
41 | "@babel/runtime": "^7.1.2",
42 | "identity-wallet": "^1.3.0",
43 | "postmsg-rpc": "^2.4.0",
44 | "store": "^2.0.12",
45 | "url-parse": "^1.4.7"
46 | },
47 | "devDependencies": {
48 | "@babel/cli": "^7.1.2",
49 | "@babel/core": "^7.1.2",
50 | "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
51 | "@babel/plugin-transform-modules-commonjs": "^7.2.0",
52 | "@babel/plugin-transform-runtime": "^7.1.0",
53 | "@babel/preset-env": "^7.1.0",
54 | "@babel/preset-react": "^7.0.0",
55 | "babel-core": "7.0.0-bridge.0",
56 | "babel-loader": "^8.0.5",
57 | "css-loader": "^2.1.1",
58 | "css-to-string-loader": "^0.1.3",
59 | "http-server": "^0.11.1",
60 | "jest": "^26.3.0",
61 | "node-sass": "^4.13.1",
62 | "sass-loader": "^8.0.2",
63 | "standard": "^12.0.1",
64 | "style-loader": "^0.23.1",
65 | "svg-inline-loader": "^0.8.2",
66 | "url-loader": "^4.0.0",
67 | "webpack": "^4.20.2",
68 | "webpack-cli": "^3.1.2"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ⚠️ ⚠️ Deprecated in favor of Ceramic ⚠️ ⚠️
2 | > 3box.js and related tools built by 3Box Labs are deprecated and no loger supported. Developers are encurraged to build with https://ceramic.network which is a more secure and decentralized protocol for sovereign data.
3 |
4 |
5 | # 3ID-Connect
6 | > ⚠️ This package has been moved to https://github.com/ceramicstudio/3id-connect
7 |
8 |
9 |
10 | 
11 |
12 | [Find 3ID-Connect on Ceramic here.](https://github.com/ceramicstudio/3id-connect)
13 |
14 | 3ID-Connect is a 3ID account management service run in an iframe. It allows you to authenicate, manage, and permission your 3ID keys to applications. Used by default in [3box-js](https://github.com/3box/3box-js). [identity-wallet-js](https://github.com/3box/identity-wallet-js) handles most operations and the parent window (application) communicates with iframe service over RPC layer as defined by [3ID JSON-RPC](https://github.com/3box/3box/blob/master/3IPs/3ip-10.md)
15 |
16 | Right now you authenticate and link ethereum accounts to mange your 3ID, in the future other keypairs, blockchain accounts, and authentication methods can be added.
17 |
18 | # 3ID-Connect Ceramic
19 |
20 | The next verion of 3ID-Connect is being developed on [Ceramic](https://ceramic.network) and [identity-wallet-js V2](https://github.com/3box/identity-wallet-js). It is being developed in parallel with the current version. You can find 3ID-Connect with Ceramic support in the [following repo](https://github.com/ceramicstudio/3id-connect). In the future this repo will be depracated. It is released at 3id-connect@next and available at 3idconnect.org.
21 |
22 | ## Structure
23 |
24 | * **/src** - Core logic and consumable interfaces for clients and iframe
25 | * **/threeIdConnect.js** - Application interface (RPC client) to load iframe and return 3ID provider.
26 | * **/threeIdConnectService.js** - Identity wallet instance and RPC 'server' to handle requests
27 | * **/threeIdProviderProxy.js** - 3ID provider interface that relays request through RPC layer
28 | * **/iframe** - all html, css, js, design assets for iframe and flow
29 | * **/public** - build assets deployed for iframe
30 |
31 | ## Development
32 |
33 | Clone and install dependencies
34 |
35 | #### Run Locally
36 |
37 | Will serve iframe locally on port 30001
38 |
39 | ```
40 | $ npm run start
41 | ```
42 |
43 | #### Build
44 |
45 | ```
46 | $ npm run build
47 | ```
48 |
49 | ## Maintainers
50 | [@zachferland](https://github.com/zachferland)
51 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2.1
2 |
3 | orbs:
4 | aws-s3: circleci/aws-s3@1.0.15
5 |
6 | jobs:
7 | test-and-build:
8 | working_directory: ~/3id-connect
9 | docker:
10 | - image: circleci/node:10
11 | steps:
12 | - checkout
13 |
14 | # Download and cache dependencies
15 | - restore_cache:
16 | keys:
17 | - dependencies-cache-{{ checksum "package.json" }}
18 |
19 | - run:
20 | name: install dependencies
21 | command: |
22 | sudo npm i -g codecov node-gyp
23 | npm i
24 |
25 | # - run:
26 | # name: test
27 | # command: npm test && codecov
28 | #
29 | # - run:
30 | # name: lint
31 | # command: npm run lint
32 |
33 | - run:
34 | name: build
35 | command: npm run build
36 |
37 | # - run:
38 | # name: code-coverage
39 | # command: bash <(curl -s https://codecov.io/bash)
40 |
41 | - persist_to_workspace:
42 | root: .
43 | paths:
44 | - public
45 |
46 | - save_cache:
47 | key: dependency-cache-{{ checksum "package.json" }}
48 | paths:
49 | - ./node_modules
50 | deploy-dev:
51 | working_directory: ~/3id-connect
52 | docker:
53 | - image: 'circleci/python:3.8'
54 | steps:
55 | - attach_workspace:
56 | at: .
57 | - aws-s3/sync:
58 | arguments: '--acl public-read --cache 604800'
59 | from: public
60 | overwrite: true
61 | to: 's3://3id-connect-dev/v1'
62 | - run:
63 | name: "Invalidate CloudFront Cache"
64 | command: aws cloudfront create-invalidation --distribution-id E3SV2UNQUEOU2O --paths /*
65 |
66 | deploy-prod:
67 | working_directory: ~/3id-connect
68 | docker:
69 | - image: 'circleci/python:3.8'
70 | steps:
71 | - attach_workspace:
72 | at: .
73 | - aws-s3/sync:
74 | arguments: '--acl public-read --cache 604800'
75 | from: public
76 | overwrite: true
77 | to: 's3://3id-connect-prod/v1'
78 | - run:
79 | name: "Invalidate CloudFront Cache"
80 | command: aws cloudfront create-invalidation --distribution-id E1CFVBE8FYHOZ0 --paths /*
81 |
82 | workflows:
83 | build-deploy:
84 | jobs:
85 | - test-and-build
86 | - deploy-dev:
87 | requires:
88 | - test-and-build
89 | # filters:
90 | # branches:
91 | # only: develop
92 | - deploy-prod:
93 | requires:
94 | - test-and-build
95 | filters:
96 | branches:
97 | only: master
98 |
--------------------------------------------------------------------------------
/src/authProvider/abstractAuthProvider.js:
--------------------------------------------------------------------------------
1 | /**
2 | * AbstractAuthProvider defines the interface your custom authProvider must
3 | * must implement. The properties network, id, name, image are all required.
4 | * The authenticate function is required. Implement your class to extend this.
5 | */
6 | class AbstractAuthProvider {
7 |
8 | /**
9 | * Creates AuthProvider instance. Must be instantiated with properties network,
10 | * id, name, image described below. Can pass any args in constructor when adding
11 | * to auth provider list.
12 | *
13 | * @param {String} network represents network, ie ethereum, bitcoin, polkadot etc.
14 | */
15 | constructor() {
16 | // TODO could network be any use still
17 | this.network = null
18 | this.isAuthProvider = true
19 | }
20 |
21 | /**
22 | * (Required) Authenticate function consumes both a message (human readable string) and
23 | * accountId (often a hex address). It is strictly required that for any
24 | * given set of {message, accountId} this function deterministically returns
25 | * a unique 32 - 64 byte hex string of entropy. This will allow this external account
26 | * to continue to access 3ID in the future and for it be added as an auth method.
27 | *
28 | * For most implementations this will be signing the message with an
29 | * account/wallet from your blockchain account provider and returning a fixed length
30 | * string of the signature by hashing it. This function does not need to consume
31 | * accountId/address if your provider knows that value at later point of the interaction.
32 | * But should still map to a unique output for any given message and accountId pair.
33 | *
34 | * For your given network/blockchain you should be able to find an authenticate
35 | * function in https://github.com/ceramicnetwork/js-3id-blockchain-utils, if you
36 | * are using the standard account signing interface in your network/blockchain.
37 | * If you are using a standard interface and it doesn't exist in js-3id-blockchain-utils,
38 | * please open an issue there, so we can add shared support for your network.
39 | *
40 | * @param {String} message A human readable string
41 | * @param {String} accountId Id of account used with provider, most often hex address
42 | * @return {String} A 32-64 bytes hex string
43 | */
44 | async authenticate(message, accountId) {
45 | throw new Error('AuthProvider must implement authenticat method')
46 | }
47 |
48 | /**
49 | * (Required) createLink will publish a public verifiable link between account
50 | * used to authenticate and the users 3ID. To implement this you need to import
51 | * createLink from https://github.com/ceramicnetwork/js-3id-blockchain-utils and
52 | * pass the link type. You must have support for your blockchain in
53 | * js-3id-blockchain-utils to add here. As other libraries need to be able to
54 | * verify and consume these links.
55 | *
56 | * @param {String} did A human readable string
57 | * @param {String} accountId Id of account used with provider, most often hex address
58 | * @return Returns on success
59 | */
60 | async createLink(did, accountId) {
61 | return null
62 | }
63 | }
64 |
65 |
66 | export default AbstractAuthProvider
67 |
--------------------------------------------------------------------------------
/iframe/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/iframe/assets/assets.js:
--------------------------------------------------------------------------------
1 | import Logo from "./logo.svg";
2 |
3 | const Loading = ``
60 |
61 |
62 | export {
63 | Logo,
64 | Loading
65 | }
66 |
--------------------------------------------------------------------------------
/src/threeIdConnect.js:
--------------------------------------------------------------------------------
1 | import ThreeIdProviderProxy from './threeIdProviderProxy.js'
2 | import { expose } from 'postmsg-rpc'
3 | import EthereumAuthProvider from './authProvider/ethereumAuthProvider.js'
4 | import { fakeIpfs } from 'identity-wallet/lib/utils'
5 |
6 | const IDENTITY_WALLET_IFRAME_URL = 'https://connect.3box.io'
7 |
8 | const HIDE_IFRAME_STYLE = 'position: fixed; width:0; height:0; border:0; border:none !important'
9 | const DISPLAY_IFRAME_STYLE = 'border:none border:0; z-index: 500; position: fixed; max-width: 100%;'
10 | const IFRAME_TOP = `top: 10px; right: 10px`
11 | const IFRAME_BOTTOM = `bottom: 0px; left: 0px;`
12 |
13 | const hide = (iframe) => () => iframe.style = HIDE_IFRAME_STYLE
14 | const display = (iframe) => (mobile = false, height = '245px', width = '440px') => iframe.style = `${DISPLAY_IFRAME_STYLE} width: ${width}; height: ${height}; ${mobile ? IFRAME_BOTTOM: IFRAME_TOP}`
15 | // TODO maybe have some more ui options here, because these can change after iframe loads
16 |
17 | /**
18 | * ThreeIdConnect provides interface for loading and instantiating IDW iframe,
19 | * and provides a 3ID provider interface to send requests to iframe. Acts like
20 | * rpc client.
21 | */
22 | class ThreeIdConnect {
23 |
24 | /**
25 | * Creates ThreeIdConnect. Create and loads iframe. Should be instantiated
26 | * on page load.
27 | *
28 | * @param {String} iframeUrl iframe url, defaults to 3id-connect iframe service
29 | */
30 | constructor (iframeUrl) {
31 | if (typeof window === 'undefined' || typeof document === 'undefined') {
32 | throw new Error('ThreeIdConnect not supported in this enviroment')
33 | }
34 |
35 | this.iframe = document.createElement('iframe')
36 | this.iframe.src = iframeUrl || IDENTITY_WALLET_IFRAME_URL
37 | this.iframe.style = HIDE_IFRAME_STYLE
38 | this.iframe.allowTransparency = true
39 | this.iframe.frameBorder = 0
40 |
41 | this.iframeLoadedPromise = new Promise((resolve, reject) => {
42 | this.iframe.onload = () => { resolve() }
43 | })
44 |
45 | document.body.appendChild(this.iframe)
46 | }
47 |
48 | // Just passing ref to threeId and ipfs during migration
49 | async connect (provider, ThreeId, ipfs) {
50 | // assumes eth provider during migration
51 | this.provider = provider
52 | this.ThreeId = ThreeId
53 | this.ipfs = ipfs
54 | // after migration, can detect different provdier to create authProvider
55 | this.authProvider = new EthereumAuthProvider(provider)
56 | }
57 |
58 | /**
59 | * Handlers to consumer message to hide or display iframe
60 | *
61 | * @private
62 | */
63 | _registerDisplayHandlers () {
64 | expose('display', display(this.iframe), {postMessage: this.postMessage})
65 | expose('hide', hide(this.iframe), {postMessage: this.postMessage})
66 | }
67 |
68 | /**
69 | * Handlers to consume messages for authProvider
70 | *
71 | * @private
72 | */
73 | _registerAuthHandlers () {
74 | expose('authenticate', this.authenticate.bind(this), {postMessage: this.postMessage})
75 | expose('migration', this.migration.bind(this), {postMessage: this.postMessage})
76 | expose('createLink', this.createLink.bind(this), {postMessage: this.postMessage})
77 | }
78 |
79 | /**
80 | * Returns ThreeId instance, used for migration of legacy 3boxjs accounts
81 | *
82 | * @private
83 | * @param {String} address An ethereum address
84 | * @return {ThreeId}
85 | */
86 | async _getThreeId (address) {
87 | if(!this._threeId) {
88 | this._threeId = await this.ThreeId.getIdFromEthAddress(address, this.provider, this.ipfs, undefined, {})
89 | }
90 | return this._threeId
91 | }
92 |
93 | async authenticate(message, address) {
94 | return this.authProvider.authenticate(message, address)
95 | }
96 |
97 | async migration(spaces, address) {
98 | const threeId = await this._getThreeId(address)
99 | await threeId.authenticate(spaces)
100 | return threeId.serializeState()
101 | }
102 |
103 | async createLink(did, address) {
104 | return this.authProvider.createLink(did, address)
105 | }
106 |
107 | /**
108 | * Returns a 3ID provider, which can send and receive 3ID messages from iframe
109 | *
110 | * @return {ThreeIdProviderProxy} A 3ID provider
111 | */
112 | async get3idProvider() {
113 | await this.iframeLoadedPromise
114 | this.postMessage = this.iframe.contentWindow.postMessage.bind(this.iframe.contentWindow)
115 | this._registerDisplayHandlers()
116 | this._registerAuthHandlers()
117 | return new ThreeIdProviderProxy(this.postMessage)
118 | }
119 | }
120 |
121 | export default ThreeIdConnect
122 |
--------------------------------------------------------------------------------
/src/threeIdConnectService.js:
--------------------------------------------------------------------------------
1 | import { expose, caller } from 'postmsg-rpc'
2 | import { fakeIpfs } from 'identity-wallet/lib/utils'
3 | const IdentityWallet = require('identity-wallet')
4 | const Url = require('url-parse')
5 | const store = require('store')
6 | const { isLinked } = require('./utils')
7 |
8 | const consentKey = (address, domain, space) => `3id_consent_${address}_${domain}_${space}`
9 | const serializedKey = (address) => `serialized3id_${address}`
10 |
11 | const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
12 | const checkIsMobile = () => mobileRegex.test(navigator.userAgent)
13 |
14 | /**
15 | * ThreeIdConnectService runs an identity wallet instance and rpc server with
16 | * bindings to receive and relay rpc messages to identity wallet
17 | */
18 | class ThreeIdConnectService {
19 |
20 | /**
21 | * Create ThreeIdConnectService
22 | */
23 | constructor () {
24 | this._registerDisplayListeners()
25 | this._registerExternalAuthListeners()
26 | }
27 |
28 | /**
29 | * Registers rpc call function for display and hiding iframe (Note: reverse of
30 | * idw rpc calls, this is rpc client, sending messages to parent window)
31 | * @private
32 | */
33 | _registerDisplayListeners () {
34 | this.display = caller('display', {postMessage: window.parent.postMessage.bind(window.parent)})
35 | this.hide = caller('hide', {postMessage: window.parent.postMessage.bind(window.parent)})
36 | }
37 |
38 |
39 | /**
40 | * Registers rpc call functions for handling external auth calls needed for IDW to parent window
41 | * @private
42 | */
43 | _registerExternalAuthListeners () {
44 | this.migration = caller('migration', {postMessage: window.parent.postMessage.bind(window.parent)})
45 | this.authenticate = caller('authenticate', {postMessage: window.parent.postMessage.bind(window.parent)})
46 | this.createLink = caller('createLink', {postMessage: window.parent.postMessage.bind(window.parent)})
47 | }
48 |
49 |
50 | /**
51 | * External Authencation method for IDW
52 | *
53 | * @param {Object} params
54 | * @param {String} params.address An ethereum address
55 | * @param {Array} params.spaces Array of space strings
56 | * @param {String} params.type Type of external auth request
57 | * @return {Object} Response depends on type of request
58 | */
59 | async externalAuth({ address, spaces, type, did }) {
60 | let threeId
61 | if (type === '3id_auth') {
62 | // TODO IMPLEMENT full migration
63 | const message = 'Add this account as a 3ID authentication method'
64 | return this.authenticate(message, address)
65 | } else if (type === '3id_migration') {
66 | let new3id
67 |
68 | const cached3id = this._get3idState(address)
69 |
70 | if (!cached3id) {
71 | this.linkPromise = isLinked(address)
72 | }
73 |
74 | const diffSpaces = this._diff3idState(cached3id, address, spaces)
75 |
76 | let migration3id
77 | if (diffSpaces) {
78 | migration3id = await this.migration(diffSpaces, address)
79 | new3id = this._merge3idState(cached3id, JSON.parse(migration3id))
80 | } else {
81 | new3id = cached3id
82 | }
83 | const new3idSerialized = JSON.stringify(new3id)
84 | this._write3idState(new3idSerialized, address)
85 | return new3idSerialized
86 | } else if (type === '3id_createLink' ) {
87 | if (this.linkPromise) {
88 | const link = await this.linkPromise
89 | if (!link) {
90 | return this.createLink(did, address)
91 | }
92 | }
93 | }
94 | }
95 |
96 | _write3idState(state, address) {
97 | store.set(serializedKey(address), state)
98 | }
99 |
100 | _get3idState(address) {
101 | const cached3id = store.get(serializedKey(address))
102 | return cached3id ? JSON.parse(cached3id) : null
103 | }
104 |
105 | _merge3idState (target, apply) {
106 | if (!target) return apply
107 | const res = Object.assign({}, target)
108 | res.spaceSeeds = Object.assign(target.spaceSeeds, apply.spaceSeeds || {})
109 | return res
110 | }
111 |
112 | _diff3idState (cached3id, address, spaces) {
113 | if (!cached3id) return spaces
114 | const cacheSpaces = Object.keys(cached3id.spaceSeeds)
115 | const diff = spaces.filter(x => !cacheSpaces.includes(x))
116 | return diff.length === 0 ? null : diff
117 | }
118 |
119 | /**
120 | * Tells parent window to display iframe
121 | */
122 | async displayIframe() {
123 | return this.display(checkIsMobile())
124 | }
125 |
126 | /**
127 | * Tells parent window to hide iframe
128 | */
129 | async hideIframe() {
130 | const root = document.getElementById('root')
131 | if (root) root.innerHTML = ``
132 | return this.hide()
133 | }
134 |
135 | /**
136 | * Removes cache consents. For partial migration in instance consent function
137 | * returns, but external auth to support consents fails. Refactored in future.
138 | *
139 | * @private
140 | * @param {Object} message IDW rpc request message
141 | * @param {String} domain Origin of caller/request
142 | * @return {ThreeId}
143 | */
144 | _removeConsents(message, domain) {
145 | const spaces = [...message.params.spaces]
146 | const rootKeys = store.get(serializedKey(message.params.address))
147 | //TODO current root 'space', name
148 | if (!rootKeys) spaces.push('undefined')
149 | spaces.forEach(space => {
150 | const key = consentKey(message.params.address, domain, space)
151 | store.remove(key)
152 | })
153 | }
154 |
155 | /**
156 | * Start identity wallet service. Once returns ready to receive rpc requests
157 | *
158 | * @param {Web3Modal} web3Modal configured instance of web3modal
159 | * @param {Function} getConsent get consent function, reference IDW
160 | * @param {Function} erroCB Function to handle errors, function consumes error string (err) => {...}, called on errors
161 | * @param {Function} cancel Function to cancel request, consumes callback, which is called when request is cancelled (cb) => {...}
162 | */
163 | start(getConsent, errorCb, cancel) {
164 | this.cancel = cancel
165 | this.errorCb = errorCb
166 | this.idWallet = new IdentityWallet(getConsent, { externalAuth: this.externalAuth.bind(this) })
167 | this.provider = this.idWallet.get3idProvider()
168 | expose('send', this.providerRelay.bind(this), {postMessage: window.parent.postMessage.bind(window.parent)})
169 | }
170 |
171 | /**
172 | * Consumes IDW RPC request message and relays to IDW instance. Also handles
173 | * logic to retry requests and cancel requests.
174 | *
175 | * @param {Object} message IDW RPC request message
176 | * @return {String} response message string
177 | */
178 | async providerRelay(message) {
179 | const domain = new Url(document.referrer).host
180 |
181 | const responsePromise = new Promise(async (resolve, reject) => {
182 | // Register request cancel calback
183 | // TODO could make all rpc errors match spec
184 | this.cancel(()=> {
185 | const res = {
186 | 'id': message.id,
187 | 'json-rpc': '2.0',
188 | error: "3id-connect: Request not authorized"
189 | }
190 | resolve(res)
191 | })
192 |
193 | if (message.method === '3id_authenticate') {
194 | try {
195 | const res = await this.provider.send(message, domain)
196 | this.hideIframe()
197 | resolve(res)
198 | } catch (e) {
199 | this.errorCb(e, 'Error: Unable to connect')
200 | this._removeConsents(message, domain)
201 | }
202 | } else {
203 | const res = await this.provider.send(message, domain)
204 | resolve(res)
205 | }
206 | })
207 |
208 | return JSON.stringify(await responsePromise)
209 | }
210 | }
211 |
212 | export default ThreeIdConnectService
213 |
--------------------------------------------------------------------------------
/iframe/css/style.scss:
--------------------------------------------------------------------------------
1 | @import './variables.scss';
2 |
3 | @font-face {
4 | font-family: 'JetBrains';
5 | src: url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/web/eot/JetBrainsMono-Regular.eot') format('embedded-opentype'),
6 | url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/web/woff2/JetBrainsMono-Regular.woff2') format('woff2'),
7 | url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/web/woff/JetBrainsMono-Regular.woff') format('woff'),
8 | url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/ttf/JetBrainsMono-Regular.ttf') format('truetype');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | h1,
14 | h2,
15 | h3,
16 | h4,
17 | h5,
18 | h6,
19 | button,
20 | p, {
21 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
22 | }
23 |
24 | h1,
25 | h2,
26 | h3,
27 | h4,
28 | h5,
29 | h6,
30 | p {
31 | padding: 0;
32 | margin: 0;
33 | }
34 |
35 | // p, span {
36 | // font-family: 'JetBrains';
37 | // }
38 |
39 | button {
40 | width: 100%;
41 | height: 40px;
42 | border-radius: $radius;
43 | font-weight: 700;
44 | display: flex;
45 | justify-content: center;
46 | align-items: center;
47 | border: none;
48 | box-shadow: 2px 3px 8px 0px #00000025;
49 |
50 | &:active:focus {
51 | outline: none !important;
52 | border: none;
53 | box-shadow: inset 1px 1px 4px #0000001a;
54 | }
55 |
56 | &:active {
57 | outline: none !important;
58 | border: none;
59 | box-shadow: 2px 3px 8px 0px #00000025;
60 | }
61 |
62 | &:focus {
63 | outline: none !important;
64 | border: none;
65 | box-shadow: 2px 3px 8px 0px #00000025;
66 | }
67 | }
68 |
69 | .primaryButton {
70 | color: $threeBoxBlue;
71 | background-color: white;
72 | border: none;
73 | }
74 |
75 | button {
76 | svg {
77 | width: 24px;
78 | height: 24px;
79 | margin-left: 6px;
80 | }
81 | }
82 |
83 | @keyframes slideInFromLeft {
84 | 0% {
85 | transform: translateX(100%);
86 | }
87 | 100% {
88 | transform: translateX(0);
89 | }
90 |
91 | }
92 |
93 | @keyframes slideInFromBottom {
94 | 0% {
95 | transform: translateY(100%);
96 | }
97 | 100% {
98 | transform: translateY(0);
99 | }
100 |
101 | }
102 |
103 | .card {
104 | width: 420px;
105 | border: 1px solid $borderColor;
106 | border-radius: $radius;
107 | display: flex;
108 | flex-direction: column;
109 | justify-content: space-between;
110 | background-color: white;
111 | box-shadow: 0px 4px 10px 1px #00000025;
112 | margin-top: 10px;
113 | margin-left: 10px;
114 | position: absolute;
115 | }
116 |
117 | @mixin slide {
118 | animation-duration: 0.7s;
119 | animation-timing-function: ease-out;
120 | animation-delay: 0s;
121 | animation-iteration-count: 1;
122 | }
123 |
124 | .slideLeft {
125 | @include slide;
126 | animation-name: slideInFromLeft;
127 | }
128 |
129 | .slideBottom {
130 | @include slide;
131 | animation-name: slideInFromBottom;
132 | }
133 |
134 |
135 | .cardMobile {
136 | width: 100vw !important;
137 | height: 100vh !important;
138 | justify-content: flex-start !important;
139 | margin-top: 20px !important;
140 | margin-left: 0px !important;
141 | }
142 |
143 | .content {
144 | width: 100%;
145 | padding-top: 5px;
146 | }
147 |
148 | .contentMobile {
149 | display: flex !important;
150 | flex-direction: column !important;
151 | justify-content: space-between !important;
152 | }
153 |
154 | .controls {
155 | width: 100%;
156 | height: 55px;
157 | // border-bottom: 1px solid $borderColor;
158 | display: flex;
159 | justify-content: space-between;
160 | align-items: center;
161 | padding: 0 14px;
162 |
163 | .return {
164 | padding: 10px;
165 |
166 | &:hover {
167 | cursor: pointer;
168 | }
169 | }
170 |
171 | .controls_logo {
172 | display: flex;
173 | justify-content: flex-start;
174 | align-items: center;
175 |
176 | svg {
177 | height: 30px;
178 | width: 30px;
179 | cursor: pointer;
180 | // transition: $transition;
181 |
182 | &:hover {
183 | opacity: 1;
184 | }
185 | }
186 |
187 | span {
188 | margin-left: 15px;
189 | font-weight: 600;
190 | color: $textColor;
191 | font-size: 14px;
192 | }
193 | }
194 |
195 | .close {
196 | padding: 0 10px;
197 | display: flex;
198 | justify-content: center;
199 | align-items: center;
200 |
201 | &:hover {
202 | cursor: pointer;
203 |
204 | .close_line {
205 | background-color: $threeBoxBlack;
206 | }
207 | }
208 |
209 | .close_line {
210 | width: 2px;
211 | height: 16px;
212 | background-color: $symbolGrey;
213 | transform: rotate(45deg);
214 | }
215 |
216 | .close_line.flip {
217 | position: absolute;
218 | transform: rotate(135deg) !important;
219 | }
220 | }
221 | }
222 |
223 | .header {
224 | width: 100%;
225 | display: flex;
226 | flex-direction: column;
227 | justify-content: center;
228 | align-items: center;
229 | }
230 |
231 | .headerLogo {
232 | width: 50px;
233 | height: 50px;
234 | border-radius: $radius;
235 | background-color: transparent;
236 | box-shadow: $dropShadow;
237 | margin-top: 25px;
238 | }
239 |
240 | .headerLogo_empty {
241 | width: 50px;
242 | height: 50px;
243 | border-radius: $radius;
244 | background-color: $threeBoxBlue;
245 | box-shadow: $dropShadow;
246 | }
247 |
248 | .walletSelect_error {
249 | color: $threeBoxRed;
250 | font-size: 13px;
251 | font-weight: 600;
252 | height: 40px;
253 | padding-top: 11px;
254 | }
255 |
256 | .headerText {
257 | padding: 30px 0px;
258 | width: 100%;
259 |
260 | .primary {
261 | color: #2a4afe;
262 | text-align: center;
263 | font-size: 20px;
264 | font-weight: 600;
265 | // font-family: 'Marcher';
266 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
267 | }
268 |
269 | .sub {
270 | text-align: center;
271 | font-size: 14px;
272 | font-weight: 400;
273 | color: #585c60;
274 | }
275 | }
276 |
277 | .headerProfile {
278 | margin-top: 20px;
279 | width: 200px;
280 | min-height: 50px;
281 | background-color: white;
282 | border-radius: 25px;
283 | border: solid #dfdfdf 2px;
284 | margin-left: auto;
285 | margin-right: auto;
286 |
287 | .img {
288 | height: 41px;
289 | width: 41px;
290 | margin: 2px;
291 | background-color: black;
292 | border-radius: 23px;
293 | float: left;
294 | }
295 |
296 | .name {
297 | float: left;
298 | padding: 12px 15px;
299 | font-weight: 500;
300 | }
301 | }
302 |
303 | .walletSelect {
304 | border: 1px solid $borderColor;
305 | box-shadow: $dropShadow;
306 | height: 62px;
307 | display: flex;
308 | justify-content: space-between;
309 | align-items: center;
310 | width: 100%;
311 | margin-bottom: 36px;
312 | border-radius: $radius;
313 | cursor: pointer;
314 | padding: 6px;
315 | }
316 |
317 | .walletSelectMobile {
318 | margin-bottom: 86px;
319 | }
320 |
321 | .walletSelect_content {
322 | margin-left: 8px;
323 | width: 100%;
324 | display: flex;
325 | justify-content: flex-start;
326 | align-items: center;
327 |
328 | h5 {
329 | width: 100%;
330 | color: $threeBoxBlue;
331 | }
332 |
333 | svg {
334 | margin-right: 12px;
335 | margin-left: 6px;
336 | height: 28px;
337 | width: 30px;
338 | }
339 | }
340 |
341 | .actions {
342 | display: flex;
343 | flex-direction: column;
344 | align-items: center;
345 | justify-content: flex-start;
346 | width: 100%;
347 | padding: 0 30px 0px 30px;
348 | position: relative;
349 | }
350 |
351 | .promptBox {
352 | padding: 20px;
353 | width: 100%;
354 | min-height: 100px;
355 | display: flex;
356 | flex-direction: column;
357 | align-items: center;
358 | justify-content: center;
359 | }
360 |
361 | .promptText {
362 | .primaryText {
363 | color: #2a4afe
364 | }
365 |
366 | .subText {
367 | position: relative;
368 |
369 | span {
370 | color: $threeBoxBlue;
371 | }
372 | }
373 | }
374 |
375 | .promptHeader {
376 | width: 100%;
377 | float: left;
378 | }
379 |
380 | .promptText {
381 | padding: 15px 30px 30px 30px;
382 |
383 | .primaryText {
384 | color: #63686d;
385 | font-weight: 600;
386 | font-size: 15px;
387 | }
388 |
389 | .primaryHighlight {
390 | color: #2a4afe;
391 | }
392 |
393 | .subText {
394 | font-weight: 400;
395 | color: $textColor;
396 | font-size: 13px;
397 | }
398 | }
399 |
400 | .onClickOutside {
401 | display: none;
402 | width: 100vw;
403 | height: 100vh;
404 | position: absolute;
405 | left: 0;
406 | top: 0;
407 | }
408 |
409 | .divider {
410 | width: 100%;
411 | border-top: solid #dfdfdf 1px;
412 | }
413 |
414 | .providerBox {
415 | width: 100%;
416 | padding: 10px;
417 | display: none;
418 | flex-direction: column;
419 | position: absolute;
420 | background-color: white;
421 | border: 1px solid $borderColor;
422 | box-shadow: $dropShadow;
423 | top: -150px;
424 | border-radius: $radius;
425 |
426 | grid-template-columns: repeat(1, 100% [col-start]);
427 | grid-row-gap: 6px;
428 | z-index: 1;
429 |
430 | .providerRow {
431 | width: 100%;
432 | float: left;
433 | padding-top: 15px;
434 | }
435 |
436 | .provider {
437 | width: 100%;
438 | display: flex;
439 | justify-content: flex-start;
440 | align-items: center;
441 | border-radius: $radius;
442 | padding: 6px;
443 |
444 | &:hover {
445 | cursor: pointer;
446 | background-color: $hoverBackground;
447 | }
448 |
449 | .providerText {
450 | font-weight: 500;
451 | }
452 | }
453 | }
454 |
455 | .providerBoxMobile {
456 | top: -110px !important;
457 | }
458 |
459 | .providerImage {
460 | display: flex;
461 | justify-content: center;
462 | align-items: center;
463 |
464 | svg {
465 | background-color: transparent;
466 | width: 40px;
467 | height: 40px;
468 | border-radius: $radius;
469 | object-fit: contain;
470 | margin-right: 15px;
471 | }
472 | }
473 |
474 | .providerImageText {
475 | font-size: 18px;
476 | }
477 |
478 | .providerBox.open {
479 | display: flex;
480 | }
481 |
482 | .spaceLine {
483 | width: 100%;
484 | padding: 10px 10px;
485 |
486 | .spaceName {}
487 |
488 | .access {
489 | float: right;
490 | color: #2a4afe;
491 | font-weight: 500;
492 | }
493 | }
494 |
495 | .footerText {
496 | font-size: 9px;
497 | padding: 25px 30px 20px 30px;
498 | color: #817972a6;
499 | text-align: center;
500 |
501 | a {
502 | color: #817972a6;
503 | &:hover {
504 | text-decoration: underline;
505 | cursor: pointer;
506 | }
507 | }
508 | }
509 |
510 | .buttonFooter {
511 | width: 100%;
512 | float: left;
513 | margin-top: 17px;
514 |
515 | .btnAllow {
516 | background-color: #2a4afe;
517 | border-color: #2e6da4;
518 | color: #ffffff;
519 | float: right;
520 | padding: 6px 25px;
521 | }
522 |
523 | .btnDecline {
524 | color: #2a4afe;
525 | float: left;
526 | margin: 7px;
527 |
528 | &:hover {
529 | cursor: pointer;
530 | }
531 | }
532 | }
533 |
534 | .marginTop25 {
535 | margin-top: 25px;
536 | }
537 |
538 | .secondaryButton {
539 | background-color: white;
540 | border: none;
541 | }
542 |
543 | #requestBox {}
544 |
545 | #requestHeader {}
546 |
547 | // @media only screen and (max-width: 600px) {
548 | // // .card {
549 | // // width: 100vw;
550 | // // height: 100vh;
551 | // // border: none;
552 | // // justify-content: flex-start;
553 | // // }
554 |
555 | // // .providerBox {
556 | // // top: -110px;
557 | // // }
558 |
559 | // // .content {
560 | // // width: 100%;
561 | // // height: 80%;
562 | // // padding-top: 125px;
563 | // // display: flex;
564 | // // flex-direction: column;
565 | // // justify-content: space-between;
566 | // // }
567 |
568 | // // .walletSelect {
569 | // // margin-bottom: 86px;
570 | // // }
571 | // }
572 |
--------------------------------------------------------------------------------