@@ -4,9 +4,10 @@ import { ShellParent, AppHomeWrapper } from '@blockstack/ui'
4
4
import { Initial , LegacyGaia } from './views'
5
5
import { bindActionCreators } from 'redux'
6
6
import { connect } from 'react-redux'
7
+ import { randomBytes } from 'crypto'
7
8
import { AuthActions } from './store/auth'
8
9
import { IdentityActions } from '../profiles/store/identity'
9
- import { decodeToken } from 'jsontokens'
10
+ import { decodeToken , TokenSigner } from 'jsontokens'
10
11
import { parseZoneFile } from 'zone-file'
11
12
import queryString from 'query-string'
12
13
import {
@@ -15,7 +16,8 @@ import {
15
16
redirectUserToApp ,
16
17
getAppBucketUrl ,
17
18
isLaterVersion ,
18
- updateQueryStringParameter
19
+ updateQueryStringParameter ,
20
+ getPublicKeyFromPrivate
19
21
} from 'blockstack'
20
22
import { AppsNode } from '@utils/account-utils'
21
23
import {
@@ -27,7 +29,10 @@ import { HDNode } from 'bitcoinjs-lib'
27
29
import log4js from 'log4js'
28
30
import { uploadProfile } from '../account/utils'
29
31
import { signProfileForUpload } from '@utils'
30
- import { validateScopes , appRequestSupportsDirectHub } from './utils'
32
+ import {
33
+ validateScopes ,
34
+ appRequestSupportsDirectHub
35
+ } from './utils'
31
36
import {
32
37
selectApi ,
33
38
selectCoreHost ,
@@ -53,6 +58,7 @@ import {
53
58
} from '@common/store/selectors/account'
54
59
import { formatAppManifest } from '@common'
55
60
import Modal from 'react-modal'
61
+ import url from 'url'
56
62
57
63
const views = [ Initial , LegacyGaia ]
58
64
@@ -86,6 +92,21 @@ function mapDispatchToProps(dispatch) {
86
92
return bindActionCreators ( actions , dispatch )
87
93
}
88
94
95
+ function makeGaiaAssociationToken ( secretKeyHex : string , childPublicKeyHex : string ) {
96
+ const LIFETIME_SECONDS = 365 * 24 * 3600
97
+ const signerKeyHex = secretKeyHex . slice ( 0 , 64 )
98
+ const compressedPublicKeyHex = getPublicKeyFromPrivate ( signerKeyHex )
99
+ const salt = randomBytes ( 16 ) . toString ( 'hex' )
100
+ const payload = { childToAssociate : childPublicKeyHex ,
101
+ iss : compressedPublicKeyHex ,
102
+ exp : LIFETIME_SECONDS + ( new Date ( ) / 1000 ) ,
103
+ iat : Date . now ( ) / 1000 ,
104
+ salt }
105
+
106
+ const token = new TokenSigner ( 'ES256K' , signerKeyHex ) . sign ( payload )
107
+ return token
108
+ }
109
+
89
110
class AuthPage extends React . Component {
90
111
static contextTypes = {
91
112
router : PropTypes . object
@@ -171,24 +192,34 @@ class AuthPage extends React.Component {
171
192
if ( redirectURI ) {
172
193
// Get the current localhost authentication url that the app will redirect back to,
173
194
// and remove the 'echo' param from it.
174
- const authContinuationURI = updateQueryStringParameter ( window . location . href , 'echo' , '' )
175
- redirectURI = updateQueryStringParameter ( redirectURI , 'echoReply' , this . state . echoRequestId )
176
- redirectURI = updateQueryStringParameter ( redirectURI , 'authContinuation' , encodeURIComponent ( authContinuationURI ) )
195
+ const authContinuationURI = updateQueryStringParameter (
196
+ window . location . href ,
197
+ 'echo' ,
198
+ ''
199
+ )
200
+ redirectURI = updateQueryStringParameter (
201
+ redirectURI ,
202
+ 'echoReply' ,
203
+ this . state . echoRequestId
204
+ )
205
+ redirectURI = updateQueryStringParameter (
206
+ redirectURI ,
207
+ 'authContinuation' ,
208
+ encodeURIComponent ( authContinuationURI )
209
+ )
177
210
} else {
178
211
throw new Error ( 'Invalid redirect echo reply URI' )
179
212
}
180
213
this . setState ( { responseSent : true } )
181
214
window . location = redirectURI
182
215
}
183
-
184
- componentWillReceiveProps ( nextProps ) {
185
216
217
+ componentWillReceiveProps ( nextProps ) {
186
218
if ( ! this . state . responseSent ) {
187
-
188
219
if ( this . state . echoRequestId ) {
189
220
this . redirectUserToEchoReply ( )
190
221
return
191
- }
222
+ }
192
223
193
224
const storageConnected = this . props . api . storageConnected
194
225
this . setState ( {
@@ -251,9 +282,7 @@ class AuthPage extends React.Component {
251
282
252
283
if ( identity . zoneFile && identity . zoneFile . length > 0 ) {
253
284
const zoneFileJson = parseZoneFile ( identity . zoneFile )
254
- const profileUrlFromZonefile = getTokenFileUrlFromZoneFile (
255
- zoneFileJson
256
- )
285
+ const profileUrlFromZonefile = getTokenFileUrlFromZoneFile ( zoneFileJson )
257
286
if (
258
287
profileUrlFromZonefile !== null &&
259
288
profileUrlFromZonefile !== undefined
@@ -267,6 +296,7 @@ class AuthPage extends React.Component {
267
296
const gaiaUrlBase = nextProps . api . gaiaHubConfig . url_prefix
268
297
269
298
if ( ! profileUrlPromise ) {
299
+ // use default Gaia hub if we can't tell from the profile where the profile Gaia hub is
270
300
profileUrlPromise = fetchProfileLocations (
271
301
gaiaUrlBase ,
272
302
identityAddress ,
@@ -358,10 +388,7 @@ class AuthPage extends React.Component {
358
388
}
359
389
360
390
getFreshIdentities = async ( ) => {
361
- await this . props . refreshIdentities (
362
- this . props . api ,
363
- this . props . addresses
364
- )
391
+ await this . props . refreshIdentities ( this . props . api , this . props . addresses )
365
392
this . setState ( { refreshingIdentities : false } )
366
393
}
367
394
@@ -392,6 +419,8 @@ class AuthPage extends React.Component {
392
419
393
420
let transitPublicKey = undefined
394
421
let hubUrl = undefined
422
+ let blockstackAPIUrl = undefined
423
+ let associationToken = undefined
395
424
396
425
let requestVersion = '0'
397
426
if ( this . state . decodedToken . payload . hasOwnProperty ( 'version' ) ) {
@@ -407,6 +436,13 @@ class AuthPage extends React.Component {
407
436
if ( appRequestSupportsDirectHub ( this . state . decodedToken . payload ) ) {
408
437
hubUrl = this . props . api . gaiaHubUrl
409
438
}
439
+ if ( isLaterVersion ( requestVersion , '1.3.0' ) ) {
440
+ const compressedAppPublicKey = getPublicKeyFromPrivate ( appPrivateKey . slice ( 0 , 64 ) )
441
+ const parsedCoreUrl = url . parse ( this . props . api . nameLookupUrl )
442
+
443
+ blockstackAPIUrl = `${ parsedCoreUrl . protocol } //${ parsedCoreUrl . host } `
444
+ associationToken = makeGaiaAssociationToken ( privateKey , compressedAppPublicKey )
445
+ }
410
446
411
447
const authResponse = makeAuthResponse (
412
448
privateKey ,
@@ -417,14 +453,17 @@ class AuthPage extends React.Component {
417
453
appPrivateKey ,
418
454
undefined ,
419
455
transitPublicKey ,
420
- hubUrl
456
+ hubUrl ,
457
+ blockstackAPIUrl ,
458
+ associationToken
421
459
)
422
460
423
461
this . props . clearSessionToken ( appDomain )
424
462
425
463
logger . info (
426
464
`login(): id index ${ this . state . currentIdentityIndex } is logging in`
427
465
)
466
+
428
467
this . setState ( { responseSent : true } )
429
468
redirectUserToApp ( this . state . authRequest , authResponse )
430
469
}
@@ -541,7 +580,11 @@ class AuthPage extends React.Component {
541
580
}
542
581
543
582
render ( ) {
544
- const { appManifest, appManifestLoading, appManifestLoadingError } = this . props
583
+ const {
584
+ appManifest,
585
+ appManifestLoading,
586
+ appManifestLoadingError
587
+ } = this . props
545
588
546
589
if ( appManifestLoadingError ) {
547
590
return (
@@ -596,11 +639,18 @@ class AuthPage extends React.Component {
596
639
)
597
640
} ,
598
641
deny : ( ) => console . log ( 'go back to app' ) ,
642
+ backLabel : 'Cancel' ,
643
+ backView : ( ) => {
644
+ if ( document . referrer === '' ) {
645
+ window . close ( )
646
+ } else {
647
+ history . back ( )
648
+ }
649
+ } ,
599
650
accounts : this . props . localIdentities ,
600
651
processing : this . state . processing ,
601
652
refreshingIdentities : this . state . refreshingIdentities ,
602
- selectedIndex : this . state . currentIdentityIndex ,
603
- disableBackOnView : 0
653
+ selectedIndex : this . state . currentIdentityIndex
604
654
}
605
655
} ,
606
656
{
@@ -630,4 +680,7 @@ class AuthPage extends React.Component {
630
680
}
631
681
}
632
682
633
- export default connect ( mapStateToProps , mapDispatchToProps ) ( AuthPage )
683
+ export default connect (
684
+ mapStateToProps ,
685
+ mapDispatchToProps
686
+ ) ( AuthPage )
0 commit comments